• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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