1[library Boost.Stacktrace 2 [quickbook 1.6] 3 [version 1.0] 4 [id stacktrace] 5 [copyright 2016-2020 Antony Polukhin] 6 [category Language Features Emulation] 7 [license 8 Distributed under the Boost Software License, Version 1.0. 9 (See accompanying file LICENSE_1_0.txt or copy at 10 [@http://www.boost.org/LICENSE_1_0.txt]) 11 ] 12] 13 14[section Motivation] 15How can one display the call sequence in C++? What function called the current function? What call sequence led to an exception? 16 17Boost.Stacktrace library is a simple C++03 library that provides information about call sequence in a human-readable form. 18 19[endsect] 20 21[section Getting Started] 22 23[import ../example/assert_handler.cpp] 24[import ../example/terminate_handler.cpp] 25[import ../example/throwing_st.cpp] 26[import ../example/trace_addresses.cpp] 27[import ../example/debug_function.cpp] 28[import ../example/user_config.hpp] 29 30[section How to print current call stack] 31 32[classref boost::stacktrace::stacktrace] contains methods for working with call-stack/backtraces/stacktraces. Here's a small example: 33``` 34#include <boost/stacktrace.hpp> 35 36// ... somewhere inside the `bar(int)` function that is called recursively: 37std::cout << boost::stacktrace::stacktrace(); 38``` 39 40In that example: 41 42* `boost::stacktrace::` is the namespace that has all the classes and functions to work with stacktraces 43* [classref boost::stacktrace::stacktrace stacktrace()] is the default constructor call; constructor stores the current function call sequence inside the stacktrace class. 44 45Code from above will output something like this: 46 47``` 48 0# bar(int) at /path/to/source/file.cpp:70 49 1# bar(int) at /path/to/source/file.cpp:70 50 2# bar(int) at /path/to/source/file.cpp:70 51 3# bar(int) at /path/to/source/file.cpp:70 52 4# main at /path/to/main.cpp:93 53 5# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6 54 6# _start 55``` 56 57[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. ] 58 59 60[endsect] 61 62[section Handle terminates, aborts and Segmentation Faults] 63 64Segmentation 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. 65 66`std::terminate` calls `std::abort`, so we need to capture stack traces on Segmentation Faults and Abort signals. 67 68[warning Writing a signal handler requires high attention! Only a few system calls allowed in signal handlers, so there's no cross platform way to print a stacktrace without a risk of deadlocking. The only way to deal with the problem - [*dump raw stacktrace into file/socket and parse it on program restart].] 69 70[warning Not all the platforms provide means for even getting stacktrace in async signal safe way. No stack trace will be saved on such platforms. ] 71 72Let's write a handler to safely dump stacktrace: 73 74[getting_started_terminate_handlers] 75 76Registering our handler: 77 78[getting_started_setup_handlers] 79 80At program start we check for a file with stacktrace and if it exist - we're writing it in human readable format: 81 82[getting_started_on_program_restart] 83 84Now we'll get the following output on `std::terminate` call after the program restarts: 85 86``` 87Previous run crashed: 88 0# 0x00007F2EC0A6A8EF 89 1# my_signal_handler(int) at ../example/terminate_handler.cpp:37 90 2# 0x00007F2EBFD84CB0 91 3# 0x00007F2EBFD84C37 92 4# 0x00007F2EBFD88028 93 5# 0x00007F2EC0395BBD 94 6# 0x00007F2EC0393B96 95 7# 0x00007F2EC0393BE1 96 8# bar(int) at ../example/terminate_handler.cpp:18 97 9# foo(int) at ../example/terminate_handler.cpp:22 9810# bar(int) at ../example/terminate_handler.cpp:14 9911# foo(int) at ../example/terminate_handler.cpp:22 10012# main at ../example/terminate_handler.cpp:84 10113# 0x00007F2EBFD6FF45 10214# 0x0000000000402209 103``` 104 105[note Function names from shared libraries may not be decoded due to address space layout randomization. Still better than nothing.] 106 107[endsect] 108 109[section Better asserts] 110 111Pretty often assertions provide not enough information to locate the problem. For example you can see the following message on out-of-range access: 112 113``` 114../../../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. 115Aborted (core dumped) 116``` 117 118That'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: 119 120[getting_started_assert_handlers] 121 122We'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]: 123 124``` 125Expression '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. 126Backtrace: 127 0# boost::assertion_failed_msg(char const*, char const*, char const*, char const*, long) at ../example/assert_handler.cpp:39 128 1# boost::array<int, 5ul>::operator[](unsigned long) at ../../../boost/array.hpp:124 129 2# bar(int) at ../example/assert_handler.cpp:17 130 3# foo(int) at ../example/assert_handler.cpp:25 131 4# bar(int) at ../example/assert_handler.cpp:17 132 5# foo(int) at ../example/assert_handler.cpp:25 133 6# main at ../example/assert_handler.cpp:54 134 7# 0x00007F991FD69F45 in /lib/x86_64-linux-gnu/libc.so.6 135 8# 0x0000000000401139 136``` 137 138Now we do know the steps that led to the assertion and can find the error without debugger. 139 140[endsect] 141 142 143[section Exceptions with stacktrace] 144 145You 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: 146 147* Declare a `boost::error_info` typedef that holds the stacktrace: 148 149[getting_started_class_traced] 150 151* Write a helper class for throwing any exception with stacktrace: 152 153[getting_started_class_with_trace] 154 155* Use `throw_with_trace(E);` instead of just `throw E;`: 156 157[getting_started_throwing_with_trace] 158 159* Process exceptions: 160 161[getting_started_catching_trace] 162 163Code from above will output: 164 165``` 166'i' must not be greater than zero in oops() 167 0# void throw_with_trace<std::logic_error>(std::logic_error const&) at ../example/throwing_st.cpp:22 168 1# oops(int) at ../example/throwing_st.cpp:38 169 2# bar(int) at ../example/throwing_st.cpp:54 170 3# foo(int) at ../example/throwing_st.cpp:59 171 4# bar(int) at ../example/throwing_st.cpp:49 172 5# foo(int) at ../example/throwing_st.cpp:59 173 6# main at ../example/throwing_st.cpp:76 174 7# 0x00007FAC113BEF45 in /lib/x86_64-linux-gnu/libc.so.6 175 8# 0x0000000000402ED9 176``` 177 178[endsect] 179 180[section Enabling and disabling stacktraces] 181 182At some point arises a requirement to easily enable/disable stacktraces for a whole project. That could be easily achieved. 183 184Just define *BOOST_STACKTRACE_LINK* for a whole project. Now you can enable/disable stacktraces by just linking with different libraries: 185 186* link with `boost_stacktrace_noop` to disable backtracing 187* link with other `boost_stacktrace_*` libraries 188 189 190See [link stacktrace.configuration_and_build section "Configuration and Build"] for more info. 191 192[endsect] 193 194[section Saving stacktraces by specified format] 195 196[classref boost::stacktrace::stacktrace] provides access to individual [classref boost::stacktrace::frame frames] of the stacktrace, 197so that you could save stacktrace information in your own format. Consider the example, that saves only function addresses of each frame: 198 199[getting_started_trace_addresses] 200 201Code from above will output: 202 203``` 2040x7fbcfd17f6b5,0x400d4a,0x400d61,0x400d61,0x400d61,0x400d61,0x400d77,0x400cbf,0x400dc0,0x7fbcfc82d830,0x400a79, 205``` 206 207[endsect] 208 209[section Getting function information from pointer] 210 211[classref boost::stacktrace::frame] provides information about functions. You may construct that class from function pointer and get the function name at runtime: 212 213[getting_started_debug_function] 214 215Code from above will output: 216 217``` 218my_signal_handler(int) at boost/libs/stacktrace/example/debug_function.cpp:21 219``` 220 221[endsect] 222 223[section Global control over stacktrace output format] 224 225You 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: 226 227[getting_started_user_config] 228 229Implementation of `do_stream_st` may be the following: 230 231[getting_started_user_config_impl] 232 233Code from above will output: 234 235``` 236Terminate called: 237 0# bar(int) 238 1# foo(int) 239 2# bar(int) 240 3# foo(int) 241``` 242 243[endsect] 244 245[/ 246[section Store stacktraces into shared memory] 247 248There's a way to serialize stacktrace in async safe manner and share that serialized representation with another process. Here's another example with signal handlers. 249 250This example is very close to the [link stacktrace.getting_started.handle_terminates_aborts_and_seg "Handle terminates, aborts and Segmentation Faults"], but this time we are dumping stacktrace into shared memory: 251 252[getting_started_terminate_handlers_shmem] 253 254After registering signal handlers and catching a signal, we may print stacktrace dumps on program restart: 255 256[getting_started_on_program_restart_shmem] 257 258The program output will be the following: 259 260``` 261Previous run crashed and left trace in shared memory: 262 0# 0x00007FD51C7218EF 263 1# my_signal_handler2(int) at ../example/terminate_handler.cpp:68 264 2# 0x00007FD51B833CB0 265 3# 0x00007FD51B833C37 266 4# 0x00007FD51B837028 267 5# 0x00007FD51BE44BBD 268 6# 0x00007FD51BE42B96 269 7# 0x00007FD51BE42BE1 270 8# bar(int) at ../example/terminate_handler.cpp:18 271 9# foo(int) at ../example/terminate_handler.cpp:22 27210# bar(int) at ../example/terminate_handler.cpp:14 27311# foo(int) at ../example/terminate_handler.cpp:22 27412# run_3(char const**) at ../example/terminate_handler.cpp:152 27513# main at ../example/terminate_handler.cpp:207 27614# 0x00007FD51B81EF45 27715# 0x0000000000402999 278``` 279 280[endsect] 281] 282 283[endsect] 284 285[section Configuration and Build] 286 287By 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: 288[table:linkmacro Link macros 289 [[Macro name] [Effect]] 290 [[*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.]] 291 [[*BOOST_STACKTRACE_DYN_LINK*] [Disable header-only build and require linking with shared library that contains tracing implementation.]] 292] 293 294 295In 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: 296[table:libconfig Config 297 [[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 `-fisibility=hidden` or marking functions as exported produce a better stacktraces.]] ] 298 [[['default for MSVC, Intel on Windows, MinGW-w64] / *BOOST_STACKTRACE_USE_WINDBG*] [*boost_stacktrace_windbg*] [ Uses COM to show debug info. May require linking with *ole32* and *dbgeng*. ] [MSVC, MinGW-w64, Intel on Windows] [yes] [no]] 299 [[['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]] 300 [[*BOOST_STACKTRACE_USE_WINDBG_CACHED*] [*boost_stacktrace_windbg_cached*] [ Uses COM to show debug info and caches COM instances in TLS for better performance. Useful only for cases when traces are gathered very often. [footnote This may affect other components of your program that use COM, because this mode calls the `CoInitializeEx(0, COINIT_MULTITHREADED)` on first use and does not call `::CoUninitialize();` until the current thread is destroyed. ] May require linking with *ole32* and *dbgeng*. ] [MSVC, Intel on Windows] [yes] [no]] 301 [[*BOOST_STACKTRACE_USE_BACKTRACE*] [*boost_stacktrace_backtrace*] [Requires linking with *libdl* on POSIX and *libbacktrace* libraries. *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. 302 303 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]] 304 [[*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]] 305 [[*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]] 306] 307 308[*Examples:] 309 310* 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*" 311* if you wish to disable backtracing and *BOOST_STACKTRACE_LINK* is defined, you just need link with *-lboost_stacktrace_noop* 312* 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 313 314[section MinGW and MinGW-w64 specific notes] 315 316MinGW-w64 and MinGW (without -w64) users have to install libbacktrace for getting better stacktraces. Follow the instruction: 317 318Let's assume that you've installed MinGW into C:\MinGW and downloaded [@https://github.com/ianlancetaylor/libbacktrace libbacktrace sources] into C:\libbacktrace-master 319 320* Configure & build libbacktrace from console: 321 * C:\MinGW\msys\1.0\bin\sh.exe 322 * cd /c/libbacktrace-master 323 * ./configure CC=/c/MinGW/bin/gcc.exe CXX=/c/MinGW/bin/g++.exe 324 * make 325 * ./libtool --mode=install /usr/bin/install -c libbacktrace.la '/c/libbacktrace-master' 326* Add info to the project-config.jam in the Boost folder: 327 * using gcc : 6 : "C:\\MinGW\\bin\\g++.exe" : <compileflags>-I"C:\\libbacktrace-master\\" <linkflags>-L"C:\\libbacktrace-master\\" ; 328* 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: 329 * b2.exe toolset=gcc-6 --with-stacktrace 330 331[endsect] 332 333[section Windows deployment and symbol files] 334 335Function names may not be resolved after deployment of your application to a different system. 336 337There are multiple ways to deal with that issue if you distribute PDB files along with your application: 338 339* 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]. 340* 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]. 341 342[endsect] 343 344[endsect] 345 346[section Acknowledgements] 347 348In order of helping and advising: 349 350* Great thanks to Bjorn Reese for highlighting the async-signal-safety issues. 351* Great thanks to Nat Goodspeed for requesting [classref boost::stacktrace::frame] like class. 352* Great thanks to Niall Douglas for making an initial review, helping with some platforms and giving great hints on library design. 353* Great thanks to all the library reviewers. 354 355[endsect] 356 357[xinclude autodoc.xml] 358 359