Compare commits

..

83 Commits

Author SHA1 Message Date
Antony Polukhin
9f68fe4e9a Make test more tolerant to filesystem faults 2017-03-17 07:25:18 +03:00
Antony Polukhin
b6fad0f575 Make sure that frames with 0x0 addresses are not stored 2017-03-16 23:10:09 +03:00
Antony Polukhin
5552c97150 MinGW fixes. Docs updated to satisfy the prereview requests 2017-03-16 22:46:21 +03:00
Antony Polukhin
3f48887f2e Another attempt to remove MSVC specific extensions (4) 2017-03-04 05:13:40 +03:00
Antony Polukhin
cdf7ef74e7 Another attempt to remove MSVC specific extensions (3) 2017-03-04 04:17:51 +03:00
Antony Polukhin
affbfef4b6 Another attempt to remove MSVC specific extensions (2) 2017-03-04 03:56:15 +03:00
Antony Polukhin
e420e0a071 Another attempt to remove MSVC specific extensions 2017-03-04 03:21:31 +03:00
Antony Polukhin
34ff2dfc04 Experimenting with MinGW and stacktracing on Windows 2017-03-03 08:36:51 +03:00
Antony Polukhin
7f45997fb2 Restore __uuidof crappy extension usage for crappy COM technology 2017-03-03 05:40:29 +03:00
Antony Polukhin
abd8afb6ec Tuning frame_msvc in attempt to use less MSVC extensions 2017-03-03 04:52:08 +03:00
Antony Polukhin
876b68d81c Fix one more issue with MinGW tests (4) 2017-03-02 00:12:34 +03:00
Antony Polukhin
6ddf49e565 Fix one more issue with MinGW tests (3) 2017-03-02 00:00:27 +03:00
Antony Polukhin
53d6ce5987 Fix one more issue with MinGW tests (2) 2017-03-01 23:46:29 +03:00
Antony Polukhin
1c6fa29299 Fix one more issue with MinGW tests 2017-03-01 23:25:28 +03:00
Antony Polukhin
4191419560 Fixing MinGW (3) 2017-02-25 23:24:48 +03:00
Antony Polukhin
7b6a8e84f7 MinGW fixes (2). Do not include unused headers 2017-02-25 23:09:36 +03:00
Antony Polukhin
94f73fe616 MinGW fixes 2017-02-24 09:07:11 +03:00
Antony Polukhin
ca6fc8b312 Typo fix for MinGW 2017-02-24 08:31:17 +03:00
Antony Polukhin
b0e04ac94d Run MinGW tests too (thanks to Pavel Filinov for showing that appveyor ability) 2017-02-24 08:21:00 +03:00
Antony Polukhin
d92c405ec1 Another attempt to fix MinGW builds 2017-02-24 08:19:50 +03:00
Antony Polukhin
6a146fe7e3 Fix MinGW compilation 2017-02-20 23:13:47 +03:00
Antony Polukhin
672a1dcbe3 Minor fixes for the docs 2017-02-14 17:30:29 +03:00
Antony Polukhin
00a13698e0 Attempt to fix MSVC tests 2017-02-07 23:26:14 +03:00
Antony Polukhin
116dd872b8 Allow users to easily skip frames (API change), add more tests and update docs 2017-02-07 22:58:22 +03:00
Antony Polukhin
eb9fcf8050 Removed internals from stacktraces, added more tests on safe dumping 2017-02-07 22:55:19 +03:00
Antony Polukhin
a246a5c148 Minor addition to the docs 2017-02-06 23:33:03 +03:00
Antony Polukhin
675ab7d65d Fix addr2line detection 2017-02-06 23:11:58 +03:00
Antony Polukhin
441d38af76 Drop version info in dump 2017-02-06 22:55:27 +03:00
Antony Polukhin
9c08e254f1 Fix MinGW compilation 2017-02-04 22:52:28 +03:00
Antony Polukhin
b842cb2284 Quickfix 2017-02-04 22:34:51 +03:00
Antony Polukhin
8050e4ea8e Better from_dump implementation (2) 2017-02-04 22:05:41 +03:00
Antony Polukhin
ab6e88f1d0 Better from_dump implementation 2017-02-04 21:40:35 +03:00
Antony Polukhin
270786eb1e Added dump format version 2017-02-04 21:22:28 +03:00
Antony Polukhin
ca0a912125 Fix issue with reading from stream 2017-02-04 02:59:31 +03:00
Antony Polukhin
fd8c0d0bc3 More debug info for the example 2017-02-04 02:43:29 +03:00
Antony Polukhin
925a90472e Fixes for signal-hdling example 2017-02-03 23:11:39 +03:00
Antony Polukhin
a462364409 Use more functions from boost::detail::winapi rather than from global namespace 2017-01-28 12:51:48 +03:00
Antony Polukhin
224750cbef Added more examples on safe_dump_to (shared memory example) and changed interface of safe dumping into the memory 2017-01-27 21:56:27 +03:00
Antony Polukhin
9146cedb94 Attempt to fix test on WIN platform (3) 2017-01-27 00:02:00 +03:00
Antony Polukhin
34fdd6077e Attempt to fix test on WIN platform (2) 2017-01-26 23:49:53 +03:00
Antony Polukhin
0875781884 Attempt to fix test on WIN platform 2017-01-26 23:38:19 +03:00
Antony Polukhin
843c6e4291 Provide more info in case of failed test 2017-01-26 23:23:51 +03:00
Antony Polukhin
ea6e7fabe9 Improve docs and move safe dumping into a separate header file 2017-01-26 22:22:40 +03:00
Antony Polukhin
d26dc67be6 Implemented safe dumping and loading 2017-01-26 22:21:24 +03:00
Antony Polukhin
d535c5a0b1 Do not take into account boost::filesystem library coverage, when counting stacktrace library coverage 2017-01-20 22:11:26 +03:00
Antony Polukhin
e4c3542c96 Even more fixes 2017-01-19 23:02:08 +03:00
Antony Polukhin
fc0063de37 More fixes for the async-safe stack dumping 2017-01-19 22:41:45 +03:00
Antony Polukhin
bc5b4fad18 MSVC fixes 2017-01-19 22:15:22 +03:00
Antony Polukhin
13fe06063b Added initial version of safe dumping 2017-01-19 21:59:37 +03:00
Antony Polukhin
c9315559a3 Improved docs by describing macro and libraries 2017-01-15 22:43:31 +03:00
Antony Polukhin
c221f0c2dd Multiple minor fixes, improved docs, changed the example to use Boost.Exception 2017-01-13 22:36:25 +03:00
Antony Polukhin
1c5274f9fa Big refactoring: no more backends, only macro to enable additional functionality 2017-01-13 22:33:37 +03:00
Antony Polukhin
bd616a7249 Make a basic usecase the default one, add tests and create a target library for that 2017-01-13 22:31:03 +03:00
Antony Polukhin
3ec1c49c3b Separate code for addr2line, libbacktrace and basic 2017-01-13 22:28:53 +03:00
Antony Polukhin
c5843350f0 Move code around 2017-01-08 21:33:23 +03:00
Antony Polukhin
7a40dc90ae Qualify all the calls, do not mix std:: and :: function usage, detail::pc_data refactored to avoid copying 2017-01-08 20:50:53 +03:00
Antony Polukhin
85c31f691d Macro for enabling/disabling addr2line usage 2017-01-08 12:05:45 +03:00
Antony Polukhin
f37eea04fa Optimize ostream operator with libbackend 2017-01-08 00:14:05 +03:00
Antony Polukhin
b7bf4b5932 Use libbacktrace, added more tests 2017-01-08 00:00:17 +03:00
Antony Polukhin
a2431640b9 Move around code in test to make Coveralls happy and produce more reliable coverage results 2017-01-03 16:09:02 +03:00
Antony Polukhin
34306df187 Added more tests with long stacktraces 2017-01-03 14:42:31 +03:00
Antony Polukhin
3f543731fa Added tests with long stacktraces 2017-01-03 14:36:01 +03:00
Antony Polukhin
c45c8ff0e4 Optimized printing stacktraces on Win 2017-01-03 14:11:30 +03:00
Antony Polukhin
053b9f5606 Typo fix for Win 2017-01-03 13:41:26 +03:00
Antony Polukhin
6de0fe088b Micro optimizations and bugfixes for Win 2017-01-03 13:27:15 +03:00
Antony Polukhin
d6c1350952 Improve backtrace formatting and output more information into the backtrace when source file info is unawailable 2017-01-03 12:58:28 +03:00
Antony Polukhin
36061d4ef8 Move code around, prepare for optimizing the stack ostreaming operators 2017-01-03 11:59:55 +03:00
Antony Polukhin
7149a04002 Optimize frame printing 2017-01-03 00:06:20 +03:00
Antony Polukhin
898380d622 Relax one of the tests 2017-01-02 23:10:52 +03:00
Antony Polukhin
5a9ba3342d Set lines count to zero on second failed attempt to get line number 2017-01-02 22:47:35 +03:00
Antony Polukhin
8f06ce9b3d Preparations for optimized streaming of frames and stacktraces 2017-01-02 21:15:25 +03:00
Antony Polukhin
0b7fae6e48 Relax one of the tests: looks like ::backtrace() call sometimes outputs itself into the stacktrace 2016-12-26 22:47:11 +03:00
Antony Polukhin
60ac93f79f Fix [move]assignemnt operators 2016-12-26 22:20:08 +03:00
Antony Polukhin
16ef077fa4 Fix unsigned overflow in basic_stacktrace constructor, improve some tests, make sure that skipping 2 frames does not show internals to the user 2016-12-26 22:18:27 +03:00
Antony Polukhin
2893578446 detail::backend class does not own data any more 2016-12-26 22:15:53 +03:00
Antony Polukhin
99e4b53742 Started reimplementing stacktrace with Allocator 2016-12-26 22:12:29 +03:00
Antony Polukhin
ec7abcdf68 Fixed MacOS examples 2016-12-26 22:11:23 +03:00
Antony Polukhin
41ed839e49 Added thanks to Bjorn Reese 2016-12-17 14:09:40 +03:00
Antony Polukhin
9d1a2d652b Call CoUninitialize after all the COM methods were used 2016-12-17 11:41:55 +03:00
Antony Polukhin
b23664a769 Added info about develop branch to the README.md 2016-12-17 11:27:02 +03:00
Antony Polukhin
b282f55342 Fix issues found by Udo Steinbach 2016-12-17 11:25:08 +03:00
Antony Polukhin
dce55ef2ef Merge pull request #9 from JonKalb/master
Better Motivation docs
2016-12-17 12:21:21 +04:00
Jon Kalb
fb5927eef8 Better Motivation docs
Clean up the wording in the Motivation section.
2016-12-16 00:12:27 -08:00
49 changed files with 2363 additions and 1291 deletions

View File

@@ -28,7 +28,7 @@ env:
# Files, which coverage results must be ignored (files from other projects).
# Example: - IGNORE_COVERAGE='*/boost/progress.hpp */filesystem/src/*'
- IGNORE_COVERAGE='*/numeric/conversion/converter_policies.hpp'
- IGNORE_COVERAGE='*/numeric/conversion/converter_policies.hpp */boost/progress.hpp */filesystem/src/*'
# Explicitly remove the following library from Boost. This may be usefull, if you're for example running Travis
# from `Boost.DLL` repo, while Boost already has `dll`.

View File

@@ -7,6 +7,7 @@ Library for storing and printing backtraces.
### Test results
@ | Build | Tests coverage | More info
----------------|-------------- | -------------- |-----------
Develop branch: | [![Build Status](https://travis-ci.org/apolukhin/stacktrace.svg?branch=develop)](https://travis-ci.org/apolukhin/stacktrace) [![Build status](https://ci.appveyor.com/api/projects/status/e070eams56vu0lm6/branch/develop?svg=true)](https://ci.appveyor.com/project/apolukhin/stacktrace/branch/develop) | [![Coverage Status](https://coveralls.io/repos/apolukhin/stacktrace/badge.png?branch=develop)](https://coveralls.io/r/apolukhin/stacktrace?branch=develop) | [details...](http://www.boost.org/development/tests/develop/developer/stacktrace.html)
Master branch: | [![Build Status](https://travis-ci.org/apolukhin/stacktrace.svg?branch=master)](https://travis-ci.org/apolukhin/stacktrace) [![Build status](https://ci.appveyor.com/api/projects/status/e070eams56vu0lm6/branch/master?svg=true)](https://ci.appveyor.com/project/apolukhin/stacktrace/branch/master) | [![Coverage Status](https://coveralls.io/repos/apolukhin/stacktrace/badge.png?branch=master)](https://coveralls.io/r/apolukhin/stacktrace?branch=master) | [details...](http://www.boost.org/development/tests/master/developer/stacktrace.html)

View File

@@ -13,7 +13,17 @@ project
lib dl : : <link>shared ;
lib gcc_s : : <link>shared ;
lib Dbghelp ;
lib Dbgeng ;
lib ole32 ;
local LIBBACKTRACE_PATH = [ modules.peek : LIBBACKTRACE_PATH ] ;
lib backtrace
:
: <search>$(LIBBACKTRACE_PATH)/lib <link>static
:
: <include>$(LIBBACKTRACE_PATH)/include
;
actions mp_simple_run_action
{
@@ -29,13 +39,13 @@ rule mp-run-simple ( sources + : args * : input-files * : requirements * : targe
alias $(target-name) : $(target-name).output ;
}
mp-run-simple has_backtrace.cpp : : : : backtrace ;
explicit backtrace ;
mp-run-simple has_backtrace.cpp : : : <library>backtrace : libbacktrace ;
explicit libbacktrace ;
mp-run-simple has_unwind.cpp : : : : unwind ;
explicit unwind ;
mp-run-simple has_addr2line.cpp : : : : addr2line ;
explicit addr2line ;
mp-run-simple has_windbg.cpp : : : : WinDbg ;
mp-run-simple has_windbg.cpp : : : <library>Dbgeng <library>ole32 : WinDbg ;
explicit WinDbg ;
lib boost_stacktrace_noop
@@ -57,8 +67,9 @@ lib boost_stacktrace_backtrace
: # requirements
<warnings>all
<target-os>linux:<library>dl
<library>backtrace
<link>shared:<define>BOOST_STACKTRACE_DYN_LINK=1
[ check-target-builds ../build//backtrace : : <build>no ]
[ check-target-builds ../build//libbacktrace : : <build>no ]
: # default build
: # usage-requirements
#<link>shared:<define>BOOST_STACKTRACE_DYN_LINK=1
@@ -66,27 +77,42 @@ lib boost_stacktrace_backtrace
boost-install boost_stacktrace_backtrace ;
lib boost_stacktrace_unwind
lib boost_stacktrace_addr2line
: # sources
../src/unwind.cpp
../src/addr2line.cpp
: # requirements
<warnings>all
<target-os>linux:<library>dl
<link>shared:<define>BOOST_STACKTRACE_DYN_LINK=1
[ check-target-builds ../build//unwind : : <build>no ]
[ check-target-builds ../build//addr2line : : <build>no ]
: # default build
: # usage-requirements
#<link>shared:<define>BOOST_STACKTRACE_DYN_LINK=1
;
boost-install boost_stacktrace_unwind ;
boost-install boost_stacktrace_addr2line ;
lib boost_stacktrace_basic
: # sources
../src/basic.cpp
: # requirements
<warnings>all
<target-os>linux:<library>dl
<link>shared:<define>BOOST_STACKTRACE_DYN_LINK=1
[ check-target-builds ../build//WinDbg : <build>no ]
: # default build
: # usage-requirements
#<link>shared:<define>BOOST_STACKTRACE_DYN_LINK=1
;
boost-install boost_stacktrace_basic ;
lib boost_stacktrace_windbg
: # sources
../src/windbg.cpp
: # requirements
<warnings>all
<library>Dbghelp
<library>Dbgeng <library>ole32
<link>shared:<define>BOOST_STACKTRACE_DYN_LINK=1
[ check-target-builds ../build//WinDbg : : <build>no ]
: # default build

16
build/has_addr2line.cpp Normal file
View File

@@ -0,0 +1,16 @@
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#include <cstdlib>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
std::string s = "addr2line -h";
return std::system(s.c_str());
}

View File

@@ -1,14 +1,14 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#include <dlfcn.h>
#include <execinfo.h>
#include <backtrace.h>
int main() {
void* buffer[10];
::backtrace(buffer, 10);
backtrace_state* state = backtrace_create_state(
0, 1, 0, 0
);
(void)state;
}

View File

@@ -1,11 +0,0 @@
// Copyright Antony Polukhin, 2016.
//
// 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)
#include <unwind.h>
int main() {
}

View File

@@ -1,16 +1,13 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#include <cstring>
#include <windows.h>
#include "Dbgeng.h"
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "Dbgeng.lib")
#include "dbgeng.h"
int main() {
CoInitializeEx(0, COINIT_MULTITHREADED);
::CoInitializeEx(0, COINIT_MULTITHREADED);
}

View File

@@ -1,4 +1,4 @@
# Copyright Antony Polukhin 2016.
# Copyright Antony Polukhin 2016-2017.
# Use, modification, and distribution are
# subject to 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)
@@ -21,9 +21,10 @@ doxygen autodoc
<doxygen:param>"PREDEFINED=\"stl_type_info=std::type_info\" \\
\"BOOST_EXPLICIT_OPERATOR_BOOL_NOEXCEPT()=explicit operator bool() const noexcept;\" \\
\"BOOST_STATIC_CONSTEXPR=static constexpr\" \\
\"BOOST_STACKTRACE_FUNCTION\" \\
\"BOOST_FORCEINLINE=inline\" \\
\"BOOST_STACKTRACE_FUNCTION=inline\" \\
\"BOOST_STACKTRACE_DOXYGEN_INVOKED\""
<xsl:param>"boost.doxygen.reftitle=Boost.Stacktrace Header Reference"
<xsl:param>"boost.doxygen.reftitle=Reference"
;
xml stacktrace : stacktrace.qbk : <dependency>autodoc ;
@@ -31,7 +32,7 @@ boostbook standalone
:
stacktrace
:
<xsl:param>boost.root=http://www.boost.org/doc/libs/1_61_0
<xsl:param>boost.root=http://www.boost.org/doc/libs/1_63_0
# <xsl:param>boost.root=../../../..
<format>pdf:<xsl:param>boost.url.prefix=http://www.boost.org/doc/libs/release/doc/html
;

View File

@@ -1,7 +1,7 @@
[library Boost.Stacktrace
[quickbook 1.6]
[version 1.0]
[copyright 2016 Antony Polukhin]
[copyright 2016-2017 Antony Polukhin]
[category Language Features Emulation]
[license
Distributed under the Boost Software License, Version 1.0.
@@ -11,11 +11,9 @@
]
[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?
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 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!]
Boost.Stacktrace library is a simple C++03 library that provides information about call sequence in a human-readable form.
[endsect]
@@ -51,62 +49,57 @@ Code from above will output something like this:
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
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 boost_stacktrace.configuration_and_build section "Configuration and Build"] for allowing advanced features of the library. ]
[endsect]
[section Handle terminates, aborts 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:
`std::terminate` calls `std::abort`, so we need to capture stack traces on Segmentation Faults and Abort signals.
[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/shared memory and parse it on program restart].]
Let's write a handler to safely dump stacktrace:
[getting_started_terminate_handlers]
After that we can set them as a default handlers and get some more information on incidents:
Registering our handler:
[getting_started_setup_handlers]
Now we'll get the following output on `std::terminate` call:
At program start we check for a file with stacktrace and if it exist - we're writing it in human readable format:
[getting_started_on_program_restart]
Now we'll get the following output on `std::terminate` call after the program restarts:
```
Terminate called:
0# boost::stacktrace::detail::backend::backend(void**, unsigned long)
1# my_terminate_handler() at boost/libs/stacktrace/example/terminate_handler.cpp:37
2# 0x7f624107a6b6
3# 0x7f624107a701
4# bar(int) at boost/libs/stacktrace/example/terminate_handler.cpp:18
5# foo(int) at boost/libs/stacktrace/example/terminate_handler.cpp:22
6# bar(int) at boost/libs/stacktrace/example/terminate_handler.cpp:18
7# foo(int) at boost/libs/stacktrace/example/terminate_handler.cpp:22
8# main at boost/libs/stacktrace/example/terminate_handler.cpp:64
9# __libc_start_main
10# _start
```
And the following output on Abort:
```
Signal 6, backtrace:
0# boost::stacktrace::detail::backend::backend(void**, unsigned long)
1# my_signal_handler(int)
2# 0x7f6240a3a4b0
3# gsignal
4# abort
5# my_terminate_handler()
6# 0x7f624107a6b6
7# 0x7f624107a701
8# bar(int) at boost/libs/stacktrace/example/terminate_handler.cpp:18
9# foo(int) at boost/libs/stacktrace/example/terminate_handler.cpp:22
10# bar(int) at boost/libs/stacktrace/example/terminate_handler.cpp:18
11# foo(int) at boost/libs/stacktrace/example/terminate_handler.cpp:22
12# main at boost/libs/stacktrace/example/terminate_handler.cpp:64
13# __libc_start_main
14# _start
Previous run crashed:
0# 0x00007F2EC0A6A8EF
1# my_signal_handler(int) at ../example/terminate_handler.cpp:37
2# 0x00007F2EBFD84CB0
3# 0x00007F2EBFD84C37
4# 0x00007F2EBFD88028
5# 0x00007F2EC0395BBD
6# 0x00007F2EC0393B96
7# 0x00007F2EC0393BE1
8# bar(int) at ../example/terminate_handler.cpp:18
9# foo(int) at ../example/terminate_handler.cpp:22
10# bar(int) at ../example/terminate_handler.cpp:14
11# foo(int) at ../example/terminate_handler.cpp:22
12# main at ../example/terminate_handler.cpp:84
13# 0x00007F2EBFD6FF45
14# 0x0000000000402209
```
The output stacktrace may be corrupted by previous actions. But now at least some basic information is available to work with.
[note Function names from shared libraries may not be decoded due to address space layout randomization. Still better than nothing.]
[endsect]
@@ -128,16 +121,15 @@ We've defined the `BOOST_ENABLE_ASSERT_DEBUG_HANDLER` macro for the whole projec
```
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::stacktrace::detail::backend::backend(void**, unsigned long)
1# boost::assertion_failed_msg(char const*, char const*, char const*, char const*, long) at boost/libs/stacktrace/example/assert_handler.cpp:38
2# boost::array<int, 5ul>::operator[](unsigned long)
3# bar(int) at boost/libs/stacktrace/example/assert_handler.cpp:16
4# foo(int) at boost/libs/stacktrace/example/assert_handler.cpp:24
5# bar(int) at boost/libs/stacktrace/example/assert_handler.cpp:20
6# foo(int) at boost/libs/stacktrace/example/assert_handler.cpp:24
7# main at boost/libs/stacktrace/example/assert_handler.cpp:53
8# __libc_start_main
9# _start
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.
@@ -147,21 +139,21 @@ Now we do know the steps that led to the assertion and can find the error withou
[section Exceptions with stacktrace]
You can provide more information along with exception by embedding stacktraces into the exception. For that you will need to:
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 doe that using Boost.Exception:
* Write a basic class that holds the stacktrace:
* Declare a `boost::error_info` typedef that holds the stacktrace:
[getting_started_class_traced]
* Write a helper class for appending class `traced` to any exception:
* Write a helper class for throwing any exception with stacktrce:
[getting_started_class_with_trace]
* Throw `with_trace<Exception>` instead of just `Exception`:
* Use `throw_with_trace(E);` instead of just `throw E;`:
[getting_started_throwing_with_trace]
* Catch exceptions by `traced`:
* Process exceptions:
[getting_started_catching_trace]
@@ -169,18 +161,15 @@ Code from above will output:
```
'i' must not be greater than zero in oops()
Backtrace:
0# boost::stacktrace::detail::backend::backend(void**, unsigned long)
1# traced::traced() at boost/libs/stacktrace/example/throwing_st.cpp:20
2# with_trace<std::logic_error>::with_trace<char const (&) [44]>(char const (&) [44]) at boost/libs/stacktrace/example/throwing_st.cpp:33
3# oops(int)
4# bar(int) at boost/libs/stacktrace/example/throwing_st.cpp:70
5# foo(int) at boost/libs/stacktrace/example/throwing_st.cpp:75
6# bar(int) at boost/libs/stacktrace/example/throwing_st.cpp:65
7# foo(int) at boost/libs/stacktrace/example/throwing_st.cpp:75
8# main at boost/libs/stacktrace/example/throwing_st.cpp:93
9# __libc_start_main
10# _start
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]
@@ -189,12 +178,13 @@ Backtrace:
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:
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 backends to output backtraces
* link with other `boost_stacktrace_*` libraries
See [link boost_stacktrace.build_macros_and_backends section "Build, Macros and Backends"] for more info.
See [link boost_stacktrace.configuration_and_build section "Configuration and Build"] for more info.
[endsect]
@@ -229,7 +219,7 @@ my_signal_handler(int) at boost/libs/stacktrace/example/debug_function.cpp:21
[section Global control over stacktrace output format]
You may control maximal stacktrace length using [macroref BOOST_STACKTRACE_DEFAULT_MAX_DEPTH] and even 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:
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]
@@ -241,45 +231,78 @@ Code from above will output:
```
Terminate called:
0# boost::stacktrace::detail::backend::backend(void**, unsigned long)
1# bar(int)
2# foo(int)
3# bar(int)
4# foo(int)
0# bar(int)
1# foo(int)
2# bar(int)
3# foo(int)
```
[endsect]
[section Store stacktraces into shared memory]
There's a way to serialize stacktrace in async safe manner and share that serialized representation with another preocess. Here's another example with signal handlers.
This example is very close to the [link boost_stacktrace.getting_started.handle_terminates_aborts_and_seg "Handle terminates, aborts and Segmentation Faults"], but this time we are dumping stacktrace into shared memory:
[getting_started_terminate_handlers_shmem]
After registring signal handlers and catching a signal, we may print stacktrace dumps on program restart:
[getting_started_on_program_restart_shmem]
The program output will be the following:
```
Previous run crashed and left trace in shared memory:
0# 0x00007FD51C7218EF
1# my_signal_handler2(int) at ../example/terminate_handler.cpp:68
2# 0x00007FD51B833CB0
3# 0x00007FD51B833C37
4# 0x00007FD51B837028
5# 0x00007FD51BE44BBD
6# 0x00007FD51BE42B96
7# 0x00007FD51BE42BE1
8# bar(int) at ../example/terminate_handler.cpp:18
9# foo(int) at ../example/terminate_handler.cpp:22
10# bar(int) at ../example/terminate_handler.cpp:14
11# foo(int) at ../example/terminate_handler.cpp:22
12# run_3(char const**) at ../example/terminate_handler.cpp:152
13# main at ../example/terminate_handler.cpp:207
14# 0x00007FD51B81EF45
15# 0x0000000000402999
```
[endsect]
[endsect]
[section Build, Macros and Backends]
[section Configuration and Build]
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:
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 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.]]
[[*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.]]
]
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
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 `-fisibility=hidden` or marking functions as exported produce a better stacktraces.]] ]
[[['default for MSVC] / *BOOST_STACKTRACE_USE_WINDBG*] [*boost_stacktrace_windbg*] [ Uses COM to show debug info. ] [Windows] [yes] [yes]]
[[['default 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.] [Not MSVC compiler on POSIX or Windows] [no] [yes]]
[[*BOOST_STACKTRACE_USE_BACKTRACE*] [*boost_stacktrace_backtrace*] [Requires linking with *libdl* on POSIX and *libbacktrace* libraries. *libbacktrace* is probably already installed in your system, or built into your compiler. Otherwise it can be downloaded [@https://github.com/gcc-mirror/gcc/tree/master/libbacktrace from here] ] [GCC/MinGW/Clang... on POSIX or Windows] [yes] [yes]]
[[*BOOST_STACKTRACE_USE_ADDR2LINE*] [*boost_stacktrace_addr2line*] [Use *addr2line* program to retrieve stacktrace. Requires linking with *libdl* library and calls `::fork`.] [GCC/MinGW/Clang... 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 you use the library in header only mode, you just need to define *BOOST_STACKTRACE_USE_NOOP* for the whole project
* if you wish to disable backtracing and *BOOST_STACKTRACE_LINK* is defined, you just need link with *-lboost_stacktrace_noop*
[endsect]
@@ -287,8 +310,10 @@ If one of the link macros is defined, you have to manually link your binary with
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.
[endsect]

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -6,6 +6,7 @@
#define BOOST_ENABLE_ASSERT_HANDLER
#include <cstdlib> // std::exit
#include <boost/array.hpp>
BOOST_NOINLINE void foo(int i);
BOOST_NOINLINE void bar(int i);

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -8,6 +8,7 @@
#include <signal.h> // ::signal
#include <boost/stacktrace/frame.hpp>
#include <iostream> // std::cerr
#include <cstdlib> // std::exit
void print_signal_handler_and_exit() {
void* p = reinterpret_cast<void*>(::signal(SIGSEGV, SIG_DFL));

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -21,47 +21,305 @@ BOOST_NOINLINE void foo(int i) {
bar(--i);
}
inline void ignore_exit(int){ std::exit(0); }
#define _Exit ignore_exit
#if defined(BOOST_GCC) && defined(BOOST_WINDOWS)
// MinGW workaround
#include <cstdlib> // ::_Exit
namespace std { using ::_Exit; }
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//[getting_started_terminate_handlers
#include <exception> // std::set_terminate, std::abort
#include <signal.h> // ::signal
#include <cstdlib> // std::_Exit
#include <boost/stacktrace.hpp>
#include <iostream> // std::cerr
void my_terminate_handler() {
std::cerr << "Terminate called:\n" << boost::stacktrace::stacktrace() << '\n';
std::abort();
}
void my_signal_handler(int signum) {
::signal(signum, SIG_DFL);
boost::stacktrace::stacktrace bt;
if (bt) {
std::cerr << "Signal " << signum << ", backtrace:\n" << boost::stacktrace::stacktrace() << '\n'; // ``[footnote Strictly speaking this code is not async-signal-safe, because it uses std::cerr. [link boost_stacktrace.build_macros_and_backends Section "Build, Macros and Backends"] describes async-signal-safe backends, so if you will use the noop backend code becomes absolutely valid as that backens always returns 0 frames and `operator<<` will be never called. ]``
}
_Exit(-1);
boost::stacktrace::safe_dump_to("./backtrace.dump");
std::_Exit(-1);
}
//]
void setup_handlers() {
//[getting_started_setup_handlers
std::set_terminate(&my_terminate_handler);
::signal(SIGSEGV, &my_signal_handler);
::signal(SIGABRT, &my_signal_handler);
//]
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int main() {
BOOST_CONSTEXPR_OR_CONST std::size_t shared_memory_size = 4096 * 8;
//[getting_started_terminate_handlers_shmem
#include <boost/stacktrace.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
boost::interprocess::shared_memory_object g_shm; // inited at program start
boost::interprocess::mapped_region g_region; // inited at program start
void my_signal_handler2(int signum) {
::signal(signum, SIG_DFL);
bool* b = static_cast<bool*>(g_region.get_address());
*b = true; // flag that memory constains stacktrace
boost::stacktrace::safe_dump_to(b + 1, g_region.get_size() - sizeof(bool));
std::_Exit(-1);
}
//]
#include <iostream> // std::cerr
#include <fstream> // std::ifstream
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
inline void copy_and_run(const char* exec_name, char param, bool not_null) {
std::cout << "Running with param " << param << std::endl;
boost::filesystem::path command = exec_name;
command = command.parent_path() / (command.stem().string() + param + command.extension().string());
boost::filesystem::copy_file(exec_name, command, boost::filesystem::copy_option::overwrite_if_exists);
boost::filesystem::path command_args = command;
command_args += ' ';
command_args += param;
const int ret = std::system(command_args.string().c_str());
std::cout << "End Running with param " << param << "; ret code is " << ret << std::endl;
boost::system::error_code ignore;
boost::filesystem::remove(command, ignore);
if (not_null && !ret) {
std::exit(97);
} else if (!not_null && ret) {
std::exit(ret);
}
}
int run_1(const char* /*argv*/[]) {
setup_handlers();
foo(5);
return 2;
return 11;
}
int run_2(const char* argv[]) {
if (!boost::filesystem::exists("./backtrace.dump")) {
if (std::string(argv[0]).find("noop") == std::string::npos) {
return 21;
}
boost::stacktrace::stacktrace st = boost::stacktrace::stacktrace::from_dump(std::cin);
if (st) {
return 22;
}
return 0;
}
//[getting_started_on_program_restart
if (boost::filesystem::exists("./backtrace.dump")) {
// there is a backtrace
std::ifstream ifs("./backtrace.dump");
boost::stacktrace::stacktrace st = boost::stacktrace::stacktrace::from_dump(ifs);
std::cout << "Previous run crashed:\n" << st << std::endl; /*<-*/
if (!st) {
return 23;
} /*->*/
// cleaning up
ifs.close();
boost::filesystem::remove("./backtrace.dump");
}
//]
return 0;
}
int run_3(const char* /*argv*/[]) {
using namespace boost::interprocess;
{
shared_memory_object shm_obj(open_or_create, "shared_memory", read_write);
shm_obj.swap(g_shm);
}
g_shm.truncate(shared_memory_size);
{
mapped_region m(g_shm, read_write, 0, shared_memory_size);
m.swap(g_region);
}
bool* b = static_cast<bool*>(g_region.get_address());
*b = false;
::signal(SIGSEGV, &my_signal_handler2);
::signal(SIGABRT, &my_signal_handler2);
foo(5);
return 31;
}
int run_4(const char* argv[]) {
using namespace boost::interprocess;
{
shared_memory_object shm_obj(open_only, "shared_memory", read_write);
shm_obj.swap(g_shm);
}
{
mapped_region m(g_shm, read_write, 0, shared_memory_size);
m.swap(g_region);
}
//[getting_started_on_program_restart_shmem
bool* b = static_cast<bool*>(g_region.get_address()); // getting flag that memory constains stacktrace
if (*b) { // checking that memory constains stacktrace
boost::stacktrace::stacktrace st
= boost::stacktrace::stacktrace::from_dump(b + 1, g_region.get_size() - sizeof(bool));
std::cout << "Previous run crashed and left trace in shared memory:\n" << st << std::endl;
*b = false; /*<-*/
shared_memory_object::remove("shared_memory");
if (std::string(argv[0]).find("noop") == std::string::npos) {
if (!st) {
return 43;
}
} else {
if (st) {
return 44;
}
}
} else {
return 42; /*->*/
}
//]
return 0;
}
#include <sstream>
int test_inplace() {
const bool is_noop = !boost::stacktrace::stacktrace();
{
boost::stacktrace::safe_dump_to("./backtrace2.dump");
boost::stacktrace::stacktrace ss2;
std::ifstream ifs("./backtrace2.dump");
boost::stacktrace::stacktrace ss1 = boost::stacktrace::stacktrace::from_dump(ifs);
ifs.close();
boost::filesystem::remove("./backtrace2.dump");
if (ss1.size() != ss2.size()) {
std::cerr << "Stacktraces differ:\n" << ss1 << "\n vs \n" << ss2 << '\n';
return 51;
}
if (ss1 && ss1[0].name() != ss2[0].name()) {
std::cerr << "Stacktraces differ:\n" << ss1 << "\n vs \n" << ss2 << '\n';
return 52;
}
}
{
void* data[1024];
boost::stacktrace::safe_dump_to(data, sizeof(data));
boost::stacktrace::stacktrace ss2;
boost::stacktrace::stacktrace ss1 = boost::stacktrace::stacktrace::from_dump(data, sizeof(data));
if (ss1.size() != ss2.size()) {
std::cerr << "Stacktraces differ:\n" << ss1 << "\n vs \n" << ss2 << '\n';
return 53;
}
if (ss1 && ss1[0].name() != ss2[0].name()) {
std::cerr << "Stacktraces differ:\n" << ss1 << "\n vs \n" << ss2 << '\n';
return 54;
}
}
{
void* data[1024];
boost::stacktrace::safe_dump_to(1024, data, sizeof(data));
if (boost::stacktrace::stacktrace::from_dump(data, sizeof(data))) {
std::cerr << "Stacktrace not empty!\n";
return 55;
}
}
{
void* data[1024];
boost::stacktrace::safe_dump_to(1, data, sizeof(data));
if (!is_noop && !boost::stacktrace::stacktrace::from_dump(data, sizeof(data))) {
std::cerr << "Stacktrace empty!\n";
return 56;
}
const std::size_t size_1_skipped = boost::stacktrace::stacktrace::from_dump(data, sizeof(data)).size();
boost::stacktrace::safe_dump_to(0, data, sizeof(data));
const std::size_t size_0_skipped = boost::stacktrace::stacktrace::from_dump(data, sizeof(data)).size();
if (!is_noop && (size_1_skipped + 1 != size_0_skipped)) {
std::cerr << "failed to skip 1 frame!\n";
return 57;
}
}
{
boost::stacktrace::safe_dump_to(0, 1, "./backtrace3.dump");
std::ifstream ifs("./backtrace3.dump");
boost::stacktrace::stacktrace ss1 = boost::stacktrace::stacktrace::from_dump(ifs);
ifs.close();
boost::stacktrace::safe_dump_to(1, 1, "./backtrace3.dump");
ifs.open("./backtrace3.dump");
boost::stacktrace::stacktrace ss2 = boost::stacktrace::stacktrace::from_dump(ifs);
ifs.close();
boost::filesystem::remove("./backtrace3.dump");
if (ss1.size() != ss2.size()) {
std::cerr << "Stacktraces differ:\n" << ss1 << "\n vs \n" << ss2 << '\n';
return 58;
}
if (!is_noop && ss1.size() != 1) {
std::cerr << "Stacktraces does not have size 1:\n" << ss1 << '\n';
return 59;
}
if (ss1 && ss1[0].address() == ss2[0].address()) {
std::cerr << "Stacktraces must differ:\n" << ss1 << "\n vs \n" << ss2 << '\n';
return 60;
}
}
return 0;
}
int main(int argc, const char* argv[]) {
if (argc < 2) {
// We are copying files to make sure that stacktrace printing works independently from executable name
copy_and_run(argv[0], '1', true);
copy_and_run(argv[0], '2', false);
copy_and_run(argv[0], '3', true);
copy_and_run(argv[0], '4', false);
return test_inplace();
}
switch (argv[1][0]) {
case '1': return run_1(argv);
case '2': return run_2(argv);
case '3': return run_3(argv);
case '4': return run_4(argv);
}
return 404;
}

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -6,37 +6,21 @@
#include <boost/config.hpp>
#if defined(BOOST_NO_CXX11_NOEXCEPT) || defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
int main(){}
#else
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//[getting_started_class_traced
#include <boost/stacktrace.hpp>
#include <boost/exception/all.hpp>
struct traced {
const boost::stacktrace::stacktrace trace;
virtual const char* what() const noexcept = 0;
virtual ~traced(){}
};
typedef boost::error_info<struct tag_stacktrace, boost::stacktrace::stacktrace> traced;
//]
//[getting_started_class_with_trace
template <class Exception>
struct with_trace : public Exception, public traced {
template <class... Args>
with_trace(Args&&... args)
: Exception(std::forward<Args>(args)...)
{}
const char* what() const noexcept {
return Exception::what();
}
};
template <class E>
void throw_with_trace(const E& e) {
throw boost::enable_error_info(e)
<< traced(boost::stacktrace::stacktrace());
}
//]
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -49,9 +33,9 @@ BOOST_NOINLINE void bar(int i);
BOOST_NOINLINE void oops(int i) {
//[getting_started_throwing_with_trace
if (i >= 4)
throw with_trace<std::out_of_range>("'i' must be less than 4 in oops()");
throw_with_trace(std::out_of_range("'i' must be less than 4 in oops()"));
if (i <= 0)
throw with_trace<std::logic_error>("'i' must not be greater than zero in oops()");
throw_with_trace(std::logic_error("'i' must not be greater than zero in oops()"));
//]
foo(i);
std::exit(1);
@@ -80,18 +64,15 @@ int main() {
//[getting_started_catching_trace
try {
foo(5); // testing assert handler
} catch (const traced& e) {
std::cerr << e.what() << '\n';
if (e.trace) {
std::cerr << "Backtrace:\n" << e.trace << '\n';
} /*<-*/ std::exit(0); /*->*/
} catch (const std::exception& e) {
std::cerr << e.what() << '\n'; /*<-*/ std::exit(3); /*->*/
std::cerr << e.what() << '\n';
const boost::stacktrace::stacktrace* st = boost::get_error_info<traced>(e);
if (st) {
std::cerr << *st << '\n'; /*<-*/ std::exit(0); /*->*/
} /*<-*/ std::exit(3); /*->*/
}
//]
return 5;
}
#endif

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -9,15 +9,14 @@
#ifndef USER_CONFIG_HPP
#define USER_CONFIG_HPP
#define BOOST_STACKTRACE_DEFAULT_MAX_DEPTH 5
#include <boost/stacktrace/stacktrace_fwd.hpp>
#include <iosfwd>
namespace boost { namespace stacktrace {
template <class CharT, class TraitsT, std::size_t Depth>
std::basic_ostream<CharT, TraitsT>& do_stream_st(std::basic_ostream<CharT, TraitsT>& os, const basic_stacktrace<Depth>& bt);
template <class CharT, class TraitsT, class Allocator>
std::basic_ostream<CharT, TraitsT>& do_stream_st(std::basic_ostream<CharT, TraitsT>& os, const basic_stacktrace<Allocator>& bt);
template <class CharT, class TraitsT>
std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const stacktrace& bt) {
@@ -33,8 +32,8 @@ std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT
//[getting_started_user_config_impl
namespace boost { namespace stacktrace {
template <class CharT, class TraitsT, std::size_t Depth>
std::basic_ostream<CharT, TraitsT>& do_stream_st(std::basic_ostream<CharT, TraitsT>& os, const basic_stacktrace<Depth>& bt) {
template <class CharT, class TraitsT, class Allocator>
std::basic_ostream<CharT, TraitsT>& do_stream_st(std::basic_ostream<CharT, TraitsT>& os, const basic_stacktrace<Allocator>& bt) {
const std::streamsize w = os.width();
const std::size_t frames = bt.size();
for (std::size_t i = 0; i < frames; ++i) {

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -14,5 +14,6 @@
#include <boost/stacktrace/frame.hpp>
#include <boost/stacktrace/stacktrace.hpp>
#include <boost/stacktrace/safe_dump_to.hpp>
#endif // BOOST_STACKTRACE_HPP

View File

@@ -1,86 +0,0 @@
// Copyright Antony Polukhin, 2016.
//
// 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)
#ifndef BOOST_STACKTRACE_CONST_ITERATOR_HPP
#define BOOST_STACKTRACE_CONST_ITERATOR_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/iterator/iterator_facade.hpp>
#include <boost/assert.hpp>
#include <boost/stacktrace/frame.hpp>
namespace boost { namespace stacktrace {
#ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED
/// Random access iterator over frames that returns boost::stacktrace::frame on dereference
class const_iterator: implementation_details {};
#else
// Forward declarations
template <std::size_t> class basic_stacktrace;
class const_iterator: public boost::iterator_facade<
const_iterator,
frame,
boost::random_access_traversal_tag,
frame>
{
typedef boost::iterator_facade<
const_iterator,
frame,
boost::random_access_traversal_tag,
frame
> base_t;
const boost::stacktrace::detail::backend* impl_;
std::size_t frame_no_;
const_iterator(const boost::stacktrace::detail::backend* impl, std::size_t frame_no) BOOST_NOEXCEPT
: impl_(impl)
, frame_no_(frame_no)
{}
template <std::size_t> friend class basic_stacktrace;
friend class ::boost::iterators::iterator_core_access;
frame dereference() const BOOST_NOEXCEPT {
return frame(impl_->get_address(frame_no_));
}
bool equal(const const_iterator& it) const BOOST_NOEXCEPT {
return impl_ == it.impl_ && frame_no_ == it.frame_no_;
}
void increment() BOOST_NOEXCEPT {
++frame_no_;
}
void decrement() BOOST_NOEXCEPT {
--frame_no_;
}
void advance(std::size_t n) BOOST_NOEXCEPT {
frame_no_ += n;
}
base_t::difference_type distance_to(const const_iterator& it) const {
BOOST_ASSERT(impl_ == it.impl_);
return it.frame_no_ - frame_no_;
}
};
#endif // #ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED
}} // namespace boost::stacktrace
#endif // BOOST_STACKTRACE_CONST_ITERATOR_HPP

View File

@@ -0,0 +1,199 @@
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_ADDR2LINE_IMPLS_HPP
#define BOOST_STACKTRACE_DETAIL_ADDR2LINE_IMPLS_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/stacktrace/detail/to_hex_array.hpp>
#include <boost/stacktrace/detail/try_demangle.hpp>
#include <boost/lexical_cast.hpp>
#include <cstdio>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
namespace boost { namespace stacktrace { namespace detail {
class addr2line_pipe {
::FILE* p;
::pid_t pid;
public:
explicit addr2line_pipe(const char *flag, const char* exec_path, const char* addr) BOOST_NOEXCEPT
: p(0)
, pid(0)
{
int pdes[2];
char prog_name[] = "addr2line";
char* argp[] = {
prog_name,
const_cast<char*>(flag),
const_cast<char*>(exec_path),
const_cast<char*>(addr),
0
};
if (::pipe(pdes) < 0) {
return;
}
pid = ::fork();
switch (pid) {
case -1:
// failed
::close(pdes[0]);
::close(pdes[1]);
return;
case 0:
// we are the child
::close(STDERR_FILENO);
::close(pdes[0]);
if (pdes[1] != STDOUT_FILENO) {
::dup2(pdes[1], STDOUT_FILENO);
}
::execvp(prog_name, argp);
::_exit(127);
}
p = ::fdopen(pdes[0], "r");
::close(pdes[1]);
}
operator ::FILE*() const BOOST_NOEXCEPT {
return p;
}
~addr2line_pipe() BOOST_NOEXCEPT {
if (p) {
::fclose(p);
int pstat = 0;
::kill(pid, SIGKILL);
::waitpid(pid, &pstat, 0);
}
}
};
inline std::string addr2line(const char* flag, const void* addr) {
std::string res;
boost::stacktrace::detail::location_from_symbol loc(addr);
if (!loc.empty()) {
res = loc.name();
} else {
res.resize(16);
int rlin_size = ::readlink("/proc/self/exe", &res[0], res.size() - 1);
while (rlin_size == static_cast<int>(res.size() - 1)) {
res.resize(res.size() * 4);
rlin_size = ::readlink("/proc/self/exe", &res[0], res.size() - 1);
}
if (rlin_size == -1) {
res.clear();
return res;
}
res.resize(rlin_size);
}
addr2line_pipe p(flag, res.c_str(), to_hex_array(addr).data());
res.clear();
if (!p) {
return res;
}
char data[32];
while (!::feof(p)) {
if (::fgets(data, sizeof(data), p)) {
res += data;
} else {
break;
}
}
// Trimming
while (!res.empty() && (res[res.size() - 1] == '\n' || res[res.size() - 1] == '\r')) {
res.erase(res.size() - 1);
}
return res;
}
struct to_string_using_addr2line {
std::string res;
void prepare_function_name(const void* addr) {
res = boost::stacktrace::frame(addr).name();
}
bool prepare_source_location(const void* addr) {
//return addr2line("-Cfipe", addr); // Does not seem to work in all cases
std::string source_line = boost::stacktrace::detail::addr2line("-Cpe", addr);
if (!source_line.empty() && source_line[0] != '?') {
res += " at ";
res += source_line;
return true;
}
return false;
}
};
template <class Base> class to_string_impl_base;
typedef to_string_impl_base<to_string_using_addr2line> to_string_impl;
inline std::string name_impl(const void* addr) {
std::string res = boost::stacktrace::detail::addr2line("-fe", addr);
res = res.substr(0, res.find_last_of('\n'));
res = boost::stacktrace::detail::try_demangle(res.c_str());
if (res == "??") {
res.clear();
}
return res;
}
} // namespace detail
std::string frame::source_file() const {
std::string res;
res = boost::stacktrace::detail::addr2line("-e", addr_);
res = res.substr(0, res.find_last_of(':'));
if (res == "??") {
res.clear();
}
return res;
}
std::size_t frame::source_line() const {
std::size_t line_num = 0;
std::string res = boost::stacktrace::detail::addr2line("-e", addr_);
const std::size_t last = res.find_last_of(':');
if (last == std::string::npos) {
return 0;
}
res = res.substr(last + 1);
if (!boost::conversion::try_lexical_convert(res, line_num)) {
return 0;
}
return line_num;
}
}} // namespace boost::stacktrace
#endif // BOOST_STACKTRACE_DETAIL_ADDR2LINE_IMPLS_HPP

View File

@@ -1,125 +0,0 @@
// Copyright Antony Polukhin, 2016.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_BACKEND_HPP
#define BOOST_STACKTRACE_DETAIL_BACKEND_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <string>
// Link or header only
#if !defined(BOOST_STACKTRACE_LINK) && defined(BOOST_STACKTRACE_DYN_LINK)
# define BOOST_STACKTRACE_LINK
#endif
#if defined(BOOST_STACKTRACE_LINK) && !defined(BOOST_STACKTRACE_DYN_LINK) && defined(BOOST_ALL_DYN_LINK)
# define BOOST_STACKTRACE_DYN_LINK
#endif
// Backend autodetection
#if !defined(BOOST_STACKTRACE_USE_NOOP) && !defined(BOOST_STACKTRACE_USE_WINDBG) && !defined(BOOST_STACKTRACE_USE_UNWIND) \
&& !defined(BOOST_STACKTRACE_USE_BACKTRACE) && !defined(BOOST_STACKTRACE_USE_HEADER)
#if defined(__has_include) && (!defined(__GNUC__) || __GNUC__ > 4 || BOOST_CLANG)
# if __has_include("Dbgeng.h")
# define BOOST_STACKTRACE_USE_WINDBG
# else
# define BOOST_STACKTRACE_USE_UNWIND
# endif
#else
# if defined(BOOST_WINDOWS)
# define BOOST_STACKTRACE_USE_WINDBG
# else
# define BOOST_STACKTRACE_USE_UNWIND
# endif
#endif
#endif
#ifdef BOOST_STACKTRACE_LINK
# if defined(BOOST_STACKTRACE_DYN_LINK)
# ifdef BOOST_STACKTRACE_INTERNAL_BUILD_LIBS
# define BOOST_STACKTRACE_FUNCTION BOOST_SYMBOL_EXPORT
# else
# define BOOST_STACKTRACE_FUNCTION BOOST_SYMBOL_IMPORT
# endif
# else
# define BOOST_STACKTRACE_FUNCTION
# endif
#else
# define BOOST_STACKTRACE_FUNCTION inline
#endif
namespace boost { namespace stacktrace { namespace detail {
// Class that implements the actual backtracing
class backend {
std::size_t hash_code_;
std::size_t frames_count_;
void** data_;
void copy_frames_from(const backend& b) BOOST_NOEXCEPT {
if (data_ == b.data_) {
return;
}
for (std::size_t i = 0; i < frames_count_; ++i) {
data_[i] = b.data_[i];
}
}
public:
BOOST_STACKTRACE_FUNCTION backend(void** memory, std::size_t size) BOOST_NOEXCEPT;
BOOST_STACKTRACE_FUNCTION static std::string get_name(const void* addr);
const void* get_address(std::size_t frame_no) const BOOST_NOEXCEPT {
return frame_no < frames_count_ ? data_[frame_no] : 0;
}
BOOST_STACKTRACE_FUNCTION static std::string get_source_file(const void* addr);
BOOST_STACKTRACE_FUNCTION static std::size_t get_source_line(const void* addr);
BOOST_STACKTRACE_FUNCTION bool operator< (const backend& rhs) const BOOST_NOEXCEPT;
BOOST_STACKTRACE_FUNCTION bool operator==(const backend& rhs) const BOOST_NOEXCEPT;
backend(const backend& b, void** memory) BOOST_NOEXCEPT
: hash_code_(b.hash_code_)
, frames_count_(b.frames_count_)
, data_(memory)
{
copy_frames_from(b);
}
backend& operator=(const backend& b) BOOST_NOEXCEPT {
hash_code_ = b.hash_code_;
frames_count_ = b.frames_count_;
copy_frames_from(b);
return *this;
}
std::size_t size() const BOOST_NOEXCEPT {
return frames_count_;
}
std::size_t hash_code() const BOOST_NOEXCEPT {
return hash_code_;
}
};
}}} // namespace boost::stacktrace::detail
/// @cond
#undef BOOST_STACKTRACE_FUNCTION
#ifndef BOOST_STACKTRACE_LINK
# include <boost/stacktrace/detail/backend.ipp>
#endif
/// @endcond
#endif // BOOST_STACKTRACE_DETAIL_BACKEND_HPP

View File

@@ -1,27 +0,0 @@
// Copyright Antony Polukhin, 2016.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_BACKEND_IPP
#define BOOST_STACKTRACE_DETAIL_BACKEND_IPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/stacktrace/detail/backend.hpp>
#if defined(BOOST_STACKTRACE_USE_NOOP)
# include <boost/stacktrace/detail/backend_noop.hpp>
#elif defined(BOOST_STACKTRACE_USE_WINDBG)
# include <boost/stacktrace/detail/backend_windows.hpp>
#elif defined(BOOST_STACKTRACE_USE_BACKTRACE) || defined(BOOST_STACKTRACE_USE_UNWIND)
# include <boost/stacktrace/detail/backend_linux.hpp>
#else
# error No suitable backtrace backend found
#endif
#endif // BOOST_STACKTRACE_DETAIL_BACKEND_IPP

View File

@@ -1,49 +0,0 @@
// Copyright Antony Polukhin, 2016.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_BACKEND_COMMON_IPP
#define BOOST_STACKTRACE_DETAIL_BACKEND_COMMON_IPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <algorithm>
namespace boost { namespace stacktrace { namespace detail {
bool backend::operator< (const backend& rhs) const BOOST_NOEXCEPT {
if (frames_count_ != rhs.frames_count_) {
return frames_count_ < rhs.frames_count_;
} else if (hash_code_ != rhs.hash_code_) {
return hash_code_ < rhs.hash_code_;
} else if (data_ == rhs.data_) {
return false;
}
return std::lexicographical_compare(
data_, data_ + frames_count_,
rhs.data_, rhs.data_ + rhs.frames_count_
);
}
bool backend::operator==(const backend& rhs) const BOOST_NOEXCEPT {
if (hash_code_ != rhs.hash_code_ || frames_count_ != rhs.frames_count_) {
return false;
} else if (data_ == rhs.data_) {
return true;
}
return std::equal(
data_, data_ + frames_count_,
rhs.data_
);
}
}}}
#endif // BOOST_STACKTRACE_DETAIL_BACKEND_COMMON_IPP

View File

@@ -1,249 +0,0 @@
// Copyright Antony Polukhin, 2016.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_BACKEND_LINUX_HPP
#define BOOST_STACKTRACE_DETAIL_BACKEND_LINUX_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/core/demangle.hpp>
#include <boost/functional/hash.hpp>
#include <boost/stacktrace/detail/to_hex_array.hpp>
#include <boost/lexical_cast/try_lexical_convert.hpp>
#if defined(BOOST_STACKTRACE_USE_UNWIND)
#include <unwind.h>
#endif
#include <dlfcn.h>
#include <execinfo.h>
#include <cstdio>
#include <sys/types.h>
#include <sys/wait.h>
namespace boost { namespace stacktrace { namespace detail {
class addr2line_pipe {
FILE* p;
pid_t pid;
public:
explicit addr2line_pipe(const char *flag, const char* exec_path, const char* addr) BOOST_NOEXCEPT
: p(0)
, pid(0)
{
int pdes[2];
char prog_name[] = "addr2line";
char* argp[] = {
prog_name,
const_cast<char*>(flag),
const_cast<char*>(exec_path),
const_cast<char*>(addr),
0
};
if (pipe(pdes) < 0) {
return;
}
pid = fork();
switch (pid) {
case -1:
// failed
close(pdes[0]);
close(pdes[1]);
return;
case 0:
// we are the child
close(STDERR_FILENO);
close(pdes[0]);
if (pdes[1] != STDOUT_FILENO) {
dup2(pdes[1], STDOUT_FILENO);
}
execvp(prog_name, argp);
_exit(127);
}
p = fdopen(pdes[0], "r");
close(pdes[1]);
}
operator FILE*() const BOOST_NOEXCEPT {
return p;
}
~addr2line_pipe() BOOST_NOEXCEPT {
if (p) {
fclose(p);
int pstat = 0;
kill(pid, SIGKILL);
waitpid(pid, &pstat, 0);
}
}
};
static inline std::string addr2line(const char* flag, const void* addr) {
std::string res;
Dl_info dli;
if (!!dladdr(addr, &dli) && dli.dli_fname) {
res = dli.dli_fname;
} else {
res.resize(16);
int rlin_size = readlink("/proc/self/exe", &res[0], res.size() - 1);
while (rlin_size == static_cast<int>(res.size() - 1)) {
res.resize(res.size() * 4);
rlin_size = readlink("/proc/self/exe", &res[0], res.size() - 1);
}
if (rlin_size == -1) {
res.clear();
return res;
}
res.resize(rlin_size);
}
addr2line_pipe p(flag, res.c_str(), to_hex_array(addr).data());
res.clear();
if (!p) {
return res;
}
char data[32];
while (!std::feof(p)) {
if (std::fgets(data, sizeof(data), p)) {
res += data;
} else {
break;
}
}
// Trimming
while (!res.empty() && (res[res.size() - 1] == '\n' || res[res.size() - 1] == '\r')) {
res.erase(res.size() - 1);
}
return res;
}
static inline std::string try_demangle(const char* mangled) {
std::string res;
boost::core::scoped_demangled_name demangled(mangled);
if (demangled.get()) {
res = demangled.get();
} else {
res = mangled;
}
return res;
}
#if defined(BOOST_STACKTRACE_USE_UNWIND)
struct unwind_state {
void** current;
void** end;
};
inline _Unwind_Reason_Code unwind_callback(struct _Unwind_Context* context, void* arg) {
unwind_state* state = static_cast<unwind_state*>(arg);
*state->current = reinterpret_cast<void*>(
_Unwind_GetIP(context)
);
++state->current;
if (!*(state->current - 1) || state->current == state->end) {
return _URC_END_OF_STACK;
}
return _URC_NO_REASON;
}
#endif
backend::backend(void** memory, std::size_t size) BOOST_NOEXCEPT
: hash_code_(0)
, frames_count_(0)
, data_(memory)
{
if (!size) {
return;
}
#if defined(BOOST_STACKTRACE_USE_UNWIND)
unwind_state state = { data_, data_ + size };
_Unwind_Backtrace(&unwind_callback, &state);
frames_count_ = state.current - data_;
#elif defined(BOOST_STACKTRACE_USE_BACKTRACE)
frames_count_ = ::backtrace(data_, size);
#else
# error No stacktrace backend defined. Define BOOST_STACKTRACE_USE_UNWIND or BOOST_STACKTRACE_USE_BACKTRACE
#endif
if (data_[frames_count_ - 1] == 0) {
-- frames_count_;
}
hash_code_ = boost::hash_range(data_, data_ + frames_count_);
}
std::string backend::get_name(const void* addr) {
std::string res;
Dl_info dli;
if (!!dladdr(addr, &dli) && dli.dli_sname) {
res = try_demangle(dli.dli_sname);
} else {
res = addr2line("-fe", addr);
res = res.substr(0, res.find_last_of('\n'));
res = try_demangle(res.c_str());
}
if (res == "??") {
res.clear();
}
return res;
}
std::string backend::get_source_file(const void* addr) {
std::string res = addr2line("-e", addr);
res = res.substr(0, res.find_last_of(':'));
if (res == "??") {
res.clear();
}
return res;
}
std::size_t backend::get_source_line(const void* addr) {
std::string res = addr2line("-e", addr);
const std::size_t last = res.find_last_of(':');
if (last == std::string::npos) {
return 0;
}
res = res.substr(last + 1);
std::size_t line_num = 0;
if (!boost::conversion::try_lexical_convert(res, line_num)) {
return 0;
}
return line_num;
}
}}} // namespace boost::stacktrace::detail
#include <boost/stacktrace/detail/backend_common.ipp>
#endif // BOOST_STACKTRACE_DETAIL_BACKEND_LINUX_HPP

View File

@@ -1,46 +0,0 @@
// Copyright Antony Polukhin, 2016.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_BACKEND_NOOP_HPP
#define BOOST_STACKTRACE_DETAIL_BACKEND_NOOP_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
namespace boost { namespace stacktrace { namespace detail {
backend::backend(void** /*memory*/, std::size_t /*size*/) BOOST_NOEXCEPT
: hash_code_(0)
, frames_count_(0)
, data_(0)
{}
std::string backend::get_name(const void* /*addr*/) {
return std::string();
}
std::string backend::get_source_file(const void* /*addr*/) {
return std::string();
}
std::size_t backend::get_source_line(const void* /*addr*/) {
return 0;
}
bool backend::operator< (const backend& /*rhs*/) const BOOST_NOEXCEPT {
return false;
}
bool backend::operator==(const backend& /*rhs*/) const BOOST_NOEXCEPT {
return true;
}
}}} // namespace boost::stacktrace::detail
#endif // BOOST_STACKTRACE_DETAIL_BACKEND_LIBUNWIND_HPP

View File

@@ -1,221 +0,0 @@
// Copyright Antony Polukhin, 2016.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_BACKEND_WINDOWS_HPP
#define BOOST_STACKTRACE_DETAIL_BACKEND_WINDOWS_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/core/noncopyable.hpp>
#include <boost/functional/hash.hpp>
#include <windows.h>
#include "Dbgeng.h"
#include <boost/detail/winapi/get_current_process.hpp>
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "Dbgeng.lib")
namespace boost { namespace stacktrace { namespace detail {
template <class T>
class com_holder: boost::noncopyable {
T* holder_;
public:
com_holder() BOOST_NOEXCEPT
: holder_(0)
{}
T* operator->() const BOOST_NOEXCEPT {
return holder_;
}
void** to_void_ptr_ptr() BOOST_NOEXCEPT {
return reinterpret_cast<void**>(&holder_);
}
bool is_inited() const BOOST_NOEXCEPT {
return !!holder_;
}
void reset() const BOOST_NOEXCEPT {
if (holder_) {
holder_->Release();
}
}
~com_holder() BOOST_NOEXCEPT {
reset();
}
};
inline bool try_init_com(com_holder<IDebugSymbols>& idebug_) BOOST_NOEXCEPT {
if (idebug_.is_inited()) {
return true;
}
CoInitializeEx(0, COINIT_MULTITHREADED);
com_holder<IDebugClient> iclient;
DebugCreate(__uuidof(IDebugClient), iclient.to_void_ptr_ptr());
com_holder<IDebugControl> icontrol;
iclient->QueryInterface(__uuidof(IDebugControl), icontrol.to_void_ptr_ptr());
const bool res1 = (S_OK == iclient->AttachProcess(
0,
GetCurrentProcessId(),
DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND)
);
if (!res1) {
return false;
}
const bool res2 = (S_OK == icontrol->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE));
if (!res2) {
return false;
}
const bool res = (S_OK == iclient->QueryInterface(__uuidof(IDebugSymbols), idebug_.to_void_ptr_ptr()));
if (!res) {
idebug_.reset();
return false;
}
return true;
}
backend::backend(void** memory, std::size_t size) BOOST_NOEXCEPT
: hash_code_(0)
, frames_count_(0)
, data_(memory)
{
boost::detail::winapi::ULONG_ hc = 0;
frames_count_ = CaptureStackBackTrace(
0,
static_cast<boost::detail::winapi::ULONG_>(size),
data_,
&hc
);
boost::hash_combine(hash_code_, hc);
}
std::string backend::get_name(const void* addr) {
std::string result;
com_holder<IDebugSymbols> idebug_;
if (!try_init_com(idebug_)) {
return result;
}
const ULONG64 offset = reinterpret_cast<ULONG64>(addr);
char name[256];
name[0] = '\0';
ULONG size = 0;
bool res = (S_OK == idebug_->GetNameByOffset(
offset,
name,
sizeof(name),
&size,
0
));
if (!res && size != 0) {
result.resize(size);
res = (S_OK == idebug_->GetNameByOffset(
offset,
&result[0],
static_cast<ULONG>(result.size()),
&size,
0
));
} else if (res) {
result = name;
}
if (!res) {
result.clear();
}
return result;
}
std::string backend::get_source_file(const void* addr) {
std::string result;
com_holder<IDebugSymbols> idebug_;
if (!try_init_com(idebug_)) {
return result;
}
const ULONG64 offset = reinterpret_cast<ULONG64>(addr);
char name[256];
name[0] = 0;
ULONG size = 0;
bool res = (S_OK == idebug_->GetLineByOffset(
offset,
0,
name,
sizeof(name),
&size,
0
));
if (!res && size != 0) {
result.resize(size);
res = (S_OK == idebug_->GetLineByOffset(
offset,
0,
&result[0],
static_cast<ULONG>(result.size()),
&size,
0
));
} else if (res) {
result = name;
}
if (!res) {
result.clear();
}
return result;
}
std::size_t backend::get_source_line(const void* addr) {
ULONG line_num = 0;
com_holder<IDebugSymbols> idebug_;
if (!try_init_com(idebug_)) {
return 0;
}
const bool is_ok = (S_OK == idebug_->GetLineByOffset(
reinterpret_cast<ULONG64>(addr),
&line_num,
0,
0,
0,
0
));
return (is_ok ? line_num : 0);
}
}}} // namespace boost::stacktrace::detail
#include <boost/stacktrace/detail/backend_common.ipp>
#endif // BOOST_STACKTRACE_DETAIL_BACKEND_LINUX_HPP

View File

@@ -0,0 +1,322 @@
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP
#define BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/stacktrace/frame.hpp>
#include <boost/core/noncopyable.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/stacktrace/detail/to_hex_array.hpp>
#include <windows.h>
#include "dbgeng.h"
#include <boost/detail/winapi/get_current_process.hpp>
#ifdef BOOST_MSVC
# pragma comment(lib, "ole32.lib")
# pragma comment(lib, "Dbgeng.lib")
#endif
#ifdef BOOST_WINDOWS
# include <boost/stacktrace/detail/safe_dump_win.ipp>
#else
# include <boost/stacktrace/detail/safe_dump_posix.ipp>
#endif
#if defined(DEFINE_GUID) && !defined(BOOST_MSVC)
DEFINE_GUID(IID_IDebugClient,0x27fe5639,0x8407,0x4f47,0x83,0x64,0xee,0x11,0x8f,0xb0,0x8a,0xc8);
DEFINE_GUID(IID_IDebugControl,0x5182e668,0x105e,0x416e,0xad,0x92,0x24,0xef,0x80,0x04,0x24,0xba);
DEFINE_GUID(IID_IDebugSymbols,0x8c31e98c,0x983a,0x48a5,0x90,0x16,0x6f,0xe5,0xd6,0x67,0xa9,0x50);
#endif
// Testing. Remove later
//# define __uuidof(x) ::IID_ ## x
namespace boost { namespace stacktrace { namespace detail {
std::size_t this_thread_frames::collect(void** memory, std::size_t size, std::size_t skip) BOOST_NOEXCEPT {
return ::CaptureStackBackTrace(
skip + 2,
static_cast<boost::detail::winapi::ULONG_>(size),
memory,
0
);
}
class com_global_initer: boost::noncopyable {
public:
com_global_initer() BOOST_NOEXCEPT {
// We do not care about the result of the function call: any result is OK for us.
::CoInitializeEx(0, COINIT_MULTITHREADED);
}
~com_global_initer() BOOST_NOEXCEPT {
::CoUninitialize();
}
};
template <class T>
class com_holder: boost::noncopyable {
T* holder_;
public:
com_holder(const com_global_initer&) BOOST_NOEXCEPT
: holder_(0)
{}
T* operator->() const BOOST_NOEXCEPT {
return holder_;
}
void** to_void_ptr_ptr() BOOST_NOEXCEPT {
return reinterpret_cast<void**>(&holder_);
}
bool is_inited() const BOOST_NOEXCEPT {
return !!holder_;
}
~com_holder() BOOST_NOEXCEPT {
if (holder_) {
holder_->Release();
}
}
};
inline bool try_init_com(com_holder<::IDebugSymbols>& idebug, const com_global_initer& com) BOOST_NOEXCEPT {
com_holder<::IDebugClient> iclient(com);
::DebugCreate(__uuidof(IDebugClient), iclient.to_void_ptr_ptr());
com_holder<::IDebugControl> icontrol(com);
iclient->QueryInterface(__uuidof(IDebugControl), icontrol.to_void_ptr_ptr());
const bool res1 = (S_OK == iclient->AttachProcess(
0,
::GetCurrentProcessId(),
DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND)
);
if (!res1) {
return false;
}
const bool res2 = (S_OK == icontrol->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE));
if (!res2) {
return false;
}
const bool res = (S_OK == iclient->QueryInterface(__uuidof(IDebugSymbols), idebug.to_void_ptr_ptr()));
if (!res) {
return false;
}
return true;
}
inline std::string get_name_impl(const com_holder<::IDebugSymbols>& idebug, const void* addr, std::string* module_name = 0) {
std::string result;
const ULONG64 offset = reinterpret_cast<ULONG64>(addr);
char name[256];
name[0] = '\0';
ULONG size = 0;
bool res = (S_OK == idebug->GetNameByOffset(
offset,
name,
sizeof(name),
&size,
0
));
if (!res && size != 0) {
result.resize(size);
res = (S_OK == idebug->GetNameByOffset(
offset,
&result[0],
static_cast<ULONG>(result.size()),
&size,
0
));
} else if (res) {
result = name;
}
if (!res) {
result.clear();
return result;
}
const std::size_t delimiter = result.find_first_of('!');
if (delimiter == std::string::npos) {
return result;
}
if (module_name) {
*module_name = result.substr(0, delimiter);
}
result = result.substr(delimiter + 1);
return result;
}
inline std::pair<std::string, std::size_t> get_source_file_line_impl(const com_holder<::IDebugSymbols>& idebug, const void* addr) {
std::pair<std::string, std::size_t> result;
const ULONG64 offset = reinterpret_cast<ULONG64>(addr);
char name[256];
name[0] = 0;
ULONG size = 0;
ULONG line_num = 0;
bool res = (S_OK == idebug->GetLineByOffset(
offset,
&line_num,
name,
sizeof(name),
&size,
0
));
if (res) {
result.first = name;
result.second = line_num;
return result;
}
if (!res && size == 0) {
return result;
}
result.first.resize(size);
res = (S_OK == idebug->GetLineByOffset(
offset,
&line_num,
&result.first[0],
static_cast<ULONG>(result.first.size()),
&size,
0
));
result.second = line_num;
if (!res) {
result.first.clear();
result.second = 0;
}
return result;
}
inline void to_string_impl(const com_holder<::IDebugSymbols>& idebug, const void* addr, std::string& res) {
std::string module_name;
std::string name = boost::stacktrace::detail::get_name_impl(idebug, addr, &module_name);
if (!name.empty()) {
res += name;
} else {
res += to_hex_array(addr).data();
}
std::pair<std::string, std::size_t> source_line
= boost::stacktrace::detail::get_source_file_line_impl(idebug, addr);
if (!source_line.first.empty() && source_line.second) {
res += " at ";
res += source_line.first;
res += ':';
res += boost::lexical_cast<boost::array<char, 40> >(source_line.second).data();
} else if (!module_name.empty()) {
res += " in ";
res += module_name;
}
}
std::string to_string(const frame* frames, std::size_t size) {
boost::stacktrace::detail::com_global_initer com_guard;
boost::stacktrace::detail::com_holder<::IDebugSymbols> idebug(com_guard);
if (!boost::stacktrace::detail::try_init_com(idebug, com_guard)) {
return std::string();
}
std::string res;
res.reserve(64 * size);
for (std::size_t i = 0; i < size; ++i) {
if (i < 10) {
res += ' ';
}
res += boost::lexical_cast<boost::array<char, 40> >(i).data();
res += '#';
res += ' ';
to_string_impl(idebug, frames[i].address(), res);
res += '\n';
}
return res;
}
} // namespace detail
std::string frame::name() const {
boost::stacktrace::detail::com_global_initer com_guard;
boost::stacktrace::detail::com_holder<::IDebugSymbols> idebug(com_guard);
if (!boost::stacktrace::detail::try_init_com(idebug, com_guard)) {
return std::string();
}
return boost::stacktrace::detail::get_name_impl(idebug, addr_);
}
std::string frame::source_file() const {
boost::stacktrace::detail::com_global_initer com_guard;
boost::stacktrace::detail::com_holder<::IDebugSymbols> idebug(com_guard);
if (!boost::stacktrace::detail::try_init_com(idebug, com_guard)) {
return std::string();
}
return boost::stacktrace::detail::get_source_file_line_impl(idebug, addr_).first;
}
std::size_t frame::source_line() const {
ULONG line_num = 0;
boost::stacktrace::detail::com_global_initer com_guard;
boost::stacktrace::detail::com_holder<::IDebugSymbols> idebug(com_guard);
if (!boost::stacktrace::detail::try_init_com(idebug, com_guard)) {
return 0;
}
const bool is_ok = (S_OK == idebug->GetLineByOffset(
reinterpret_cast<ULONG64>(addr_),
&line_num,
0,
0,
0,
0
));
return (is_ok ? line_num : 0);
}
std::string to_string(const frame& f) {
boost::stacktrace::detail::com_global_initer com_guard;
boost::stacktrace::detail::com_holder<::IDebugSymbols> idebug(com_guard);
if (!boost::stacktrace::detail::try_init_com(idebug, com_guard)) {
return std::string();
}
std::string res;
boost::stacktrace::detail::to_string_impl(idebug, f.address(), res);
return res;
}
}} // namespace boost::stacktrace
#endif // BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP

View File

@@ -0,0 +1,63 @@
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_FRAME_NOOP_IPP
#define BOOST_STACKTRACE_DETAIL_FRAME_NOOP_IPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/stacktrace/frame.hpp>
namespace boost { namespace stacktrace { namespace detail {
std::string to_string(const frame* /*frames*/, std::size_t /*count*/) {
return std::string();
}
std::size_t this_thread_frames::collect(void** /*memory*/, std::size_t /*size*/, std::size_t /*skip*/) BOOST_NOEXCEPT {
return 0;
}
#if defined(BOOST_WINDOWS)
std::size_t dump(void* /*fd*/, void** /*memory*/, std::size_t /*size*/) BOOST_NOEXCEPT {
return 0;
}
#else
std::size_t dump(int /*fd*/, void** /*memory*/, std::size_t /*size*/) BOOST_NOEXCEPT {
return 0;
}
#endif
std::size_t dump(const char* /*file*/, void** /*memory*/, std::size_t /*size*/) BOOST_NOEXCEPT {
return 0;
}
} // namespace detail
std::string frame::name() const {
return std::string();
}
std::string frame::source_file() const {
return std::string();
}
std::size_t frame::source_line() const {
return 0;
}
std::string to_string(const frame& /*f*/) {
return std::string();
}
}} // namespace boost::stacktrace
#endif // BOOST_STACKTRACE_DETAIL_FRAME_NOOP_IPP

View File

@@ -0,0 +1,151 @@
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_FRAME_UNWIND_IPP
#define BOOST_STACKTRACE_DETAIL_FRAME_UNWIND_IPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/stacktrace/frame.hpp>
#include <boost/stacktrace/detail/to_hex_array.hpp>
#include <boost/stacktrace/detail/try_demangle.hpp>
#include <boost/stacktrace/detail/location_from_symbol.hpp>
#include <boost/lexical_cast.hpp>
#include <unwind.h>
#include <cstdio>
#ifdef BOOST_STACKTRACE_USE_BACKTRACE
# include <boost/stacktrace/detail/libbacktrace_impls.hpp>
#elif defined(BOOST_STACKTRACE_USE_ADDR2LINE)
# include <boost/stacktrace/detail/addr2line_impls.hpp>
#else
# include <boost/stacktrace/detail/unwind_base_impls.hpp>
#endif
#ifdef BOOST_WINDOWS
# include <boost/stacktrace/detail/safe_dump_win.ipp>
#else
# include <boost/stacktrace/detail/safe_dump_posix.ipp>
#endif
namespace boost { namespace stacktrace { namespace detail {
struct unwind_state {
std::size_t frames_to_skip;
void** current;
void** end;
};
inline _Unwind_Reason_Code unwind_callback(::_Unwind_Context* context, void* arg) {
unwind_state* state = static_cast<unwind_state*>(arg);
if (state->frames_to_skip) {
--state->frames_to_skip;
return ::_Unwind_GetIP(context) ? ::_URC_NO_REASON : ::_URC_END_OF_STACK;
}
*state->current = reinterpret_cast<void*>(
::_Unwind_GetIP(context)
);
++state->current;
if (!*(state->current - 1) || state->current == state->end) {
return ::_URC_END_OF_STACK;
}
return ::_URC_NO_REASON;
}
std::size_t this_thread_frames::collect(void** memory, std::size_t size, std::size_t skip) BOOST_NOEXCEPT {
std::size_t frames_count = 0;
if (!size) {
return frames_count;
}
boost::stacktrace::detail::unwind_state state = { skip + 1, memory, memory + size };
::_Unwind_Backtrace(&boost::stacktrace::detail::unwind_callback, &state);
frames_count = state.current - memory;
if (frames_count && memory[frames_count - 1] == 0) {
-- frames_count;
}
return frames_count;
}
template <class Base>
class to_string_impl_base: private Base {
public:
std::string operator()(const void* addr) {
Base::res.clear();
Base::prepare_function_name(addr);
if (!Base::res.empty()) {
Base::res = boost::stacktrace::detail::try_demangle(Base::res.c_str());
} else {
Base::res = to_hex_array(addr).data();
}
if (Base::prepare_source_location(addr)) {
return Base::res;
}
boost::stacktrace::detail::location_from_symbol loc(addr);
if (!loc.empty()) {
Base::res += " in ";
Base::res += loc.name();
}
return Base::res;
}
};
std::string to_string(const frame* frames, std::size_t size) {
std::string res;
res.reserve(64 * size);
to_string_impl impl;
for (std::size_t i = 0; i < size; ++i) {
if (i < 10) {
res += ' ';
}
res += boost::lexical_cast<boost::array<char, 40> >(i).data();
res += '#';
res += ' ';
res += impl(frames[i].address());
res += '\n';
}
return res;
}
} // namespace detail
std::string frame::name() const {
#ifndef BOOST_WINDOWS
::Dl_info dli;
const bool dl_ok = !!::dladdr(addr_, &dli);
if (dl_ok && dli.dli_sname) {
return boost::stacktrace::detail::try_demangle(dli.dli_sname);
}
#endif
return boost::stacktrace::detail::name_impl(addr_);
}
std::string to_string(const frame& f) {
boost::stacktrace::detail::to_string_impl impl;
return impl(f.address());
}
}} // namespace boost::stacktrace
#endif // BOOST_STACKTRACE_DETAIL_FRAME_UNWIND_IPP

View File

@@ -0,0 +1,122 @@
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_LIBBACKTRACE_IMPLS_HPP
#define BOOST_STACKTRACE_DETAIL_LIBBACKTRACE_IMPLS_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/stacktrace/detail/to_hex_array.hpp>
#include <boost/stacktrace/detail/try_demangle.hpp>
#include <boost/lexical_cast.hpp>
#include <backtrace.h>
namespace boost { namespace stacktrace { namespace detail {
struct pc_data {
std::string* function;
std::string* filename;
std::size_t line;
};
inline int libbacktrace_full_callback(void *data, uintptr_t /*pc*/, const char *filename, int lineno, const char *function) {
pc_data& d = *static_cast<pc_data*>(data);
if (d.filename && filename) {
*d.filename = filename;
}
if (d.function && function) {
*d.function = function;
}
d.line = lineno;
return 0;
}
struct to_string_using_backtrace {
std::string res;
::backtrace_state* state;
std::string filename;
std::size_t line;
void prepare_function_name(const void* addr) {
boost::stacktrace::detail::pc_data data = {&res, &filename, 0};
::backtrace_pcinfo(state, reinterpret_cast<uintptr_t>(addr), boost::stacktrace::detail::libbacktrace_full_callback, 0, &data);
line = data.line;
}
bool prepare_source_location(const void* /*addr*/) {
if (filename.empty() || !line) {
return false;
}
res += " at ";
res += filename;
res += ':';
res += boost::lexical_cast<boost::array<char, 40> >(line).data();
return true;
}
to_string_using_backtrace() BOOST_NOEXCEPT {
state = ::backtrace_create_state(
0, 0, 0, 0
);
}
};
template <class Base> class to_string_impl_base;
typedef to_string_impl_base<to_string_using_backtrace> to_string_impl;
inline std::string name_impl(const void* addr) {
std::string res;
::backtrace_state* state = ::backtrace_create_state(
0, 0, 0, 0
);
boost::stacktrace::detail::pc_data data = {&res, 0, 0};
::backtrace_pcinfo(state, reinterpret_cast<uintptr_t>(addr), boost::stacktrace::detail::libbacktrace_full_callback, 0, &data);
if (!res.empty()) {
res = boost::stacktrace::detail::try_demangle(res.c_str());
}
return res;
}
} // namespace detail
std::string frame::source_file() const {
std::string res;
::backtrace_state* state = ::backtrace_create_state(
0, 0, 0, 0
);
boost::stacktrace::detail::pc_data data = {0, &res, 0};
::backtrace_pcinfo(state, reinterpret_cast<uintptr_t>(addr_), boost::stacktrace::detail::libbacktrace_full_callback, 0, &data);
return res;
}
std::size_t frame::source_line() const {
::backtrace_state* state = ::backtrace_create_state(
0, 0, 0, 0
);
boost::stacktrace::detail::pc_data data = {0, 0, 0};
::backtrace_pcinfo(state, reinterpret_cast<uintptr_t>(addr_), boost::stacktrace::detail::libbacktrace_full_callback, 0, &data);
return data.line;
}
}} // namespace boost::stacktrace
#endif // BOOST_STACKTRACE_DETAIL_LIBBACKTRACE_IMPLS_HPP

View File

@@ -0,0 +1,76 @@
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_LOCATION_FROM_SYMBOL_HPP
#define BOOST_STACKTRACE_DETAIL_LOCATION_FROM_SYMBOL_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#ifndef BOOST_WINDOWS
# include <dlfcn.h>
#else
# include <boost/detail/winapi/dll.hpp>
#endif
namespace boost { namespace stacktrace { namespace detail {
class location_from_symbol {
#ifndef BOOST_WINDOWS
::Dl_info dli_;
public:
explicit location_from_symbol(const void* addr) BOOST_NOEXCEPT
: dli_()
{
if (!::dladdr(addr, &dli_)) {
dli_.dli_fname = 0;
}
}
bool empty() const BOOST_NOEXCEPT {
return !dli_.dli_fname;
}
const char* name() const BOOST_NOEXCEPT {
return dli_.dli_fname;
}
#else
BOOST_STATIC_CONSTEXPR boost::detail::winapi::DWORD_ DEFAULT_PATH_SIZE_ = 260;
char file_name_[DEFAULT_PATH_SIZE_];
public:
explicit location_from_symbol(const void* addr) BOOST_NOEXCEPT {
file_name_[0] = '\0';
boost::detail::winapi::MEMORY_BASIC_INFORMATION_ mbi;
if (!boost::detail::winapi::VirtualQuery(addr, &mbi, sizeof(mbi))) {
return;
}
boost::detail::winapi::HMODULE_ handle = reinterpret_cast<boost::detail::winapi::HMODULE_>(mbi.AllocationBase);
if (!boost::detail::winapi::GetModuleFileNameA(handle, file_name_, DEFAULT_PATH_SIZE_)) {
file_name_[0] = '\0';
return;
}
}
bool empty() const BOOST_NOEXCEPT {
return file_name_[0] == '\0';
}
const char* name() const BOOST_NOEXCEPT {
return file_name_;
}
#endif
};
}}} // namespace boost::stacktrace::detail
#endif // BOOST_STACKTRACE_DETAIL_LOCATION_FROM_SYMBOL_HPP

View File

@@ -0,0 +1,48 @@
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_SAFE_DUMP_POSIX_IPP
#define BOOST_STACKTRACE_DETAIL_SAFE_DUMP_POSIX_IPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/stacktrace/frame.hpp>
#include <unistd.h> // ::write
#include <fcntl.h> // ::open
namespace boost { namespace stacktrace { namespace detail {
std::size_t dump(int fd, void** memory, std::size_t size) BOOST_NOEXCEPT {
// We do not retry, because this function must be typically called from signal handler so it's:
// * to scary to continue in case of EINTR
// * EAGAIN or EWOULDBLOCK may occur only in case of O_NONBLOCK is set for fd,
// so it seems that user does not want to block
if (::write(fd, memory, sizeof(void*) * size) == -1) {
return 0;
}
return size;
}
std::size_t dump(const char* file, void** memory, std::size_t mem_size) BOOST_NOEXCEPT {
const int fd = ::open(file, O_CREAT | O_WRONLY | O_TRUNC, S_IFREG | S_IWUSR | S_IRUSR);
if (fd == -1) {
return 0;
}
const std::size_t size = boost::stacktrace::detail::dump(fd, memory, mem_size);
::close(fd);
return size;
}
}}} // namespace boost::stacktrace::detail
#endif // BOOST_STACKTRACE_DETAIL_SAFE_DUMP_POSIX_IPP

View File

@@ -0,0 +1,58 @@
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_SAFE_DUMP_WIN_IPP
#define BOOST_STACKTRACE_DETAIL_SAFE_DUMP_WIN_IPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/stacktrace/frame.hpp>
#include <boost/core/noncopyable.hpp>
#include <boost/detail/winapi/get_current_process.hpp>
#include <boost/detail/winapi/file_management.hpp>
#include <boost/detail/winapi/handles.hpp>
#include <boost/detail/winapi/access_rights.hpp>
#include <windows.h> // CaptureStackBackTrace
namespace boost { namespace stacktrace { namespace detail {
std::size_t dump(void* fd, void** memory, std::size_t mem_size) BOOST_NOEXCEPT {
if (!boost::detail::winapi::WriteFile(fd, memory, static_cast<boost::detail::winapi::DWORD_>(sizeof(void*) * mem_size), 0, 0)) {
return 0;
}
return mem_size;
}
std::size_t dump(const char* file, void** memory, std::size_t mem_size) BOOST_NOEXCEPT {
void* const fd = boost::detail::winapi::CreateFileA(
file,
boost::detail::winapi::GENERIC_WRITE_,
0,
0,
boost::detail::winapi::CREATE_ALWAYS_,
boost::detail::winapi::FILE_ATTRIBUTE_NORMAL_,
0
);
if (fd == boost::detail::winapi::invalid_handle_value) {
return 0;
}
const std::size_t size = boost::stacktrace::detail::dump(fd, memory, mem_size);
boost::detail::winapi::CloseHandle(fd);
return size;
}
}}} // namespace boost::stacktrace::detail
#endif // BOOST_STACKTRACE_DETAIL_SAFE_DUMP_WIN_IPP

View File

@@ -1,11 +1,11 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_STACKTRACE_TO_HEX_ARRAY_HPP
#define BOOST_STACKTRACE_DETAIL_STACKTRACE_TO_HEX_ARRAY_HPP
#ifndef BOOST_STACKTRACE_DETAIL_TO_HEX_ARRAY_HPP
#define BOOST_STACKTRACE_DETAIL_TO_HEX_ARRAY_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
@@ -51,4 +51,4 @@ inline boost::array<char, 2 + sizeof(void*) * 2 + 1> to_hex_array(const void* ad
}}} // namespace boost::stacktrace::detail
#endif // BOOST_STACKTRACE_DETAIL_STACKTRACE_TO_HEX_ARRAY_HPP
#endif // BOOST_STACKTRACE_DETAIL_TO_HEX_ARRAY_HPP

View File

@@ -0,0 +1,35 @@
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_TRY_DEMANGLE_HPP
#define BOOST_STACKTRACE_DETAIL_TRY_DEMANGLE_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/core/demangle.hpp>
namespace boost { namespace stacktrace { namespace detail {
inline std::string try_demangle(const char* mangled) {
std::string res;
boost::core::scoped_demangled_name demangled(mangled);
if (demangled.get()) {
res = demangled.get();
} else {
res = mangled;
}
return res;
}
}}} // namespace boost::stacktrace::detail
#endif // BOOST_STACKTRACE_DETAIL_TRY_DEMANGLE_HPP

View File

@@ -0,0 +1,50 @@
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#ifndef BOOST_STACKTRACE_DETAIL_UNWIND_BASE_IMPLS_HPP
#define BOOST_STACKTRACE_DETAIL_UNWIND_BASE_IMPLS_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/stacktrace/frame.hpp>
namespace boost { namespace stacktrace { namespace detail {
struct to_string_using_nothing {
std::string res;
void prepare_function_name(const void* addr) {
res = boost::stacktrace::frame(addr).name();
}
bool prepare_source_location(const void* /*addr*/) const BOOST_NOEXCEPT {
return false;
}
};
template <class Base> class to_string_impl_base;
typedef to_string_impl_base<to_string_using_nothing> to_string_impl;
inline std::string name_impl(const void* /*addr*/) {
return std::string();
}
} // namespace detail
std::string frame::source_file() const {
return std::string();
}
std::size_t frame::source_line() const {
return 0;
}
}} // namespace boost::stacktrace
#endif // BOOST_STACKTRACE_DETAIL_UNWIND_BASE_IMPLS_HPP

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -16,10 +16,55 @@
#include <string>
#include <boost/core/explicit_operator_bool.hpp>
#include <boost/stacktrace/detail/backend.hpp>
// Link or header only
#if !defined(BOOST_STACKTRACE_LINK) && defined(BOOST_STACKTRACE_DYN_LINK)
# define BOOST_STACKTRACE_LINK
#endif
#if defined(BOOST_STACKTRACE_LINK) && !defined(BOOST_STACKTRACE_DYN_LINK) && defined(BOOST_ALL_DYN_LINK)
# define BOOST_STACKTRACE_DYN_LINK
#endif
#ifdef BOOST_STACKTRACE_LINK
# if defined(BOOST_STACKTRACE_DYN_LINK)
# ifdef BOOST_STACKTRACE_INTERNAL_BUILD_LIBS
# define BOOST_STACKTRACE_FUNCTION BOOST_SYMBOL_EXPORT
# else
# define BOOST_STACKTRACE_FUNCTION BOOST_SYMBOL_IMPORT
# endif
# else
# define BOOST_STACKTRACE_FUNCTION
# endif
#elif !defined(BOOST_STACKTRACE_DOXYGEN_INVOKED)
# define BOOST_STACKTRACE_FUNCTION inline
#endif
namespace boost { namespace stacktrace {
class frame;
namespace detail {
BOOST_STACKTRACE_FUNCTION std::string to_string(const frame* frames, std::size_t size);
enum helper{ max_frames_dump = 128 };
BOOST_STACKTRACE_FUNCTION std::size_t from_dump(const char* filename, void** frames);
BOOST_STACKTRACE_FUNCTION std::size_t dump(const char* file, void** memory, std::size_t size) BOOST_NOEXCEPT;
#if defined(BOOST_WINDOWS)
BOOST_STACKTRACE_FUNCTION std::size_t dump(void* fd, void** memory, std::size_t size) BOOST_NOEXCEPT;
#else
// POSIX
BOOST_STACKTRACE_FUNCTION std::size_t dump(int fd, void** memory, std::size_t size) BOOST_NOEXCEPT;
#endif
struct this_thread_frames { // struct is required to avoid warning about usage of inline+BOOST_NOINLINE
BOOST_NOINLINE BOOST_STACKTRACE_FUNCTION static std::size_t collect(void** memory, std::size_t size, std::size_t skip) BOOST_NOEXCEPT;
};
} // namespace detail
/// Non-owning class that references the frame information stored inside the boost::stacktrace::stacktrace class.
class frame {
/// @cond
@@ -28,7 +73,7 @@ class frame {
public:
/// @brief Constructs frame that references NULL address.
/// Calls to source_file() and source_line() wil lreturn empty string.
/// Calls to source_file() and source_line() will return empty string.
/// Calls to source_line() will return 0.
///
/// @b Complexity: O(1).
@@ -57,7 +102,7 @@ public:
frame& operator=(const frame&) = default;
#endif
/// @brief Constructs frame that can extract information from addr at runtime.
/// @brief Constructs frame that references addr and could later generate information about that address using platform specific features.
///
/// @b Complexity: O(1).
///
@@ -69,14 +114,16 @@ public:
/// @returns Name of the frame (function name in a human readable form).
///
/// @b Complexity: unknown (lots of platform specific work).
///
/// @b Async-Handler-Safety: Unsafe.
/// @throws std::bad_alloc if not enough memory to construct resulting string.
std::string name() const {
return boost::stacktrace::detail::backend::get_name(address());
}
BOOST_STACKTRACE_FUNCTION std::string name() const;
/// @returns Address of the frame function.
///
/// @b Complexity: O(1).
///
/// @b Async-Handler-Safety: Safe.
/// @throws Nothing.
const void* address() const BOOST_NOEXCEPT {
@@ -87,18 +134,18 @@ public:
/// if this->source_line() == 0.
/// @throws std::bad_alloc if not enough memory to construct resulting string.
///
/// @b Complexity: unknown (lots of platform specific work).
///
/// @b Async-Handler-Safety: Unsafe.
std::string source_file() const {
return boost::stacktrace::detail::backend::get_source_file(address());
}
BOOST_STACKTRACE_FUNCTION std::string source_file() const;
/// @returns Code line in the source file, were the function of the frame is defined.
/// @throws std::bad_alloc if not enough memory to construct string for internal needs.
///
/// @b Complexity: unknown (lots of platform specific work).
///
/// @b Async-Handler-Safety: Unsafe.
std::size_t source_line() const {
return boost::stacktrace::detail::backend::get_source_line(address());
}
BOOST_STACKTRACE_FUNCTION std::size_t source_line() const;
/// @brief Checks that frame is not references NULL address.
/// @returns `true` if `this->address() != 0`
@@ -129,29 +176,36 @@ inline bool operator>=(const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { retu
inline bool operator==(const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return lhs.address() == rhs.address(); }
inline bool operator!=(const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return !(lhs == rhs); }
/// Hashing support, O(1) complexity; Async-Handler-Safe.
/// Fast hashing support, O(1) complexity; Async-Handler-Safe.
inline std::size_t hash_value(const frame& f) BOOST_NOEXCEPT {
return reinterpret_cast<std::size_t>(f.address());
}
/// Outputs stacktrace::frame in a human readable format to string; unsafe to use in async handlers.
BOOST_STACKTRACE_FUNCTION std::string to_string(const frame& f);
/// Outputs stacktrace::frame in a human readable format to output stream; unsafe to use in async handlers.
template <class CharT, class TraitsT>
std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const frame& f) {
std::string name = f.name();
if (!name.empty()) {
os << name;
} else {
os << f.address();
}
const std::size_t source_line = f.source_line();
if (source_line) {
os << " at " << f.source_file() << ':' << source_line;
}
return os;
return os << boost::stacktrace::to_string(f);
}
}} // namespace boost::stacktrace
/// @cond
#undef BOOST_STACKTRACE_FUNCTION
#ifndef BOOST_STACKTRACE_LINK
# if defined(BOOST_STACKTRACE_USE_NOOP)
# include <boost/stacktrace/detail/frame_noop.ipp>
# elif defined(BOOST_MSVC) || defined(BOOST_STACKTRACE_USE_WINDBG)
# include <boost/stacktrace/detail/frame_msvc.ipp>
# else
# include <boost/stacktrace/detail/frame_unwind.ipp>
# endif
#endif
/// @endcond
#endif // BOOST_STACKTRACE_FRAME_HPP

View File

@@ -0,0 +1,166 @@
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#ifndef BOOST_STACKTRACE_SAFE_DUMP_TO_HPP
#define BOOST_STACKTRACE_SAFE_DUMP_TO_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/stacktrace/frame.hpp>
/// @file safe_dump_to.hpp This header contains low-level async-signal-safe functions for dumping call stacks. Dumps are binary serialized arrays of `void*`,
/// so you could read them by using 'od -tx8 -An stacktrace_dump_failename' Linux command or using boost::stacktrace::stacktrace::from_dump functions.
namespace boost { namespace stacktrace {
/// @cond
namespace detail {
struct suppress_noinline_warnings {
BOOST_NOINLINE static std::size_t safe_dump_to_impl(void* memory, std::size_t size, std::size_t skip) BOOST_NOEXCEPT {
void** mem = static_cast<void**>(memory);
const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(mem, size / sizeof(void*) - 1, skip + 1);
mem[frames_count] = 0;
return frames_count + 1;
}
template <class T>
BOOST_NOINLINE static std::size_t safe_dump_to_impl(T file, std::size_t skip, std::size_t max_depth) BOOST_NOEXCEPT {
void* buffer[boost::stacktrace::detail::max_frames_dump + 1];
if (max_depth > boost::stacktrace::detail::max_frames_dump) {
max_depth = boost::stacktrace::detail::max_frames_dump;
}
const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(buffer, max_depth, skip + 1);
buffer[frames_count] = 0;
return boost::stacktrace::detail::dump(file, buffer, frames_count + 1);
}
};
}
/// @endcond
/// @brief Stores current function call sequence into the memory.
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe.
///
/// @returns Stored call sequence depth.
///
/// @param memory Preallocated buffer to store current function call sequence into.
///
/// @param size Size of the preallocated buffer.
BOOST_FORCEINLINE std::size_t safe_dump_to(void* memory, std::size_t size) BOOST_NOEXCEPT {
return boost::stacktrace::detail::suppress_noinline_warnings::safe_dump_to_impl(memory, size, 0);
}
/// @brief Stores current function call sequence into the memory.
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe.
///
/// @returns Stored call sequence depth.
///
/// @param skip How many top calls to skip and do not store.
///
/// @param memory Preallocated buffer to store current function call sequence into.
///
/// @param size Size of the preallocated buffer.
BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, void* memory, std::size_t size) BOOST_NOEXCEPT {
return boost::stacktrace::detail::suppress_noinline_warnings::safe_dump_to_impl(memory, size, skip);
}
/// @brief Opens a file and rewrites its content with current function call sequence.
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe.
///
/// @returns Stored call sequence depth.
///
/// @param file File to store current function call sequence.
BOOST_FORCEINLINE std::size_t safe_dump_to(const char* file) BOOST_NOEXCEPT {
return boost::stacktrace::detail::suppress_noinline_warnings::safe_dump_to_impl(file, 0, boost::stacktrace::detail::max_frames_dump);
}
/// @brief Opens a file and rewrites its content with current function call sequence.
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe.
///
/// @returns Stored call sequence depth.
///
/// @param skip How many top calls to skip and do not store.
///
/// @param max_depth Max call sequence depth to collect.
///
/// @param file File to store current function call sequence.
BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, std::size_t max_depth, const char* file) BOOST_NOEXCEPT {
return boost::stacktrace::detail::suppress_noinline_warnings::safe_dump_to_impl(file, skip, max_depth);
}
#ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED
/// @brief Writes into the provided file descriptor the current function call sequence.
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe.
///
/// @returns Stored call sequence depth.
///
/// @param file File to store current function call sequence.
BOOST_FORCEINLINE std::size_t safe_dump_to(platform_specific_descriptor fd) BOOST_NOEXCEPT;
/// @brief Writes into the provided file descriptor the current function call sequence.
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe.
///
/// @returns Stored call sequence depth.
///
/// @param skip How many top calls to skip and do not store.
///
/// @param max_depth Max call sequence depth to collect.
///
/// @param file File to store current function call sequence.
BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, std::size_t max_depth, platform_specific_descriptor fd) BOOST_NOEXCEPT;
#elif defined(BOOST_WINDOWS)
BOOST_FORCEINLINE std::size_t safe_dump_to(void* fd) BOOST_NOEXCEPT {
return boost::stacktrace::detail::suppress_noinline_warnings::safe_dump_to_impl(fd, 0, boost::stacktrace::detail::max_frames_dump);
}
BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, std::size_t max_depth, void* fd) BOOST_NOEXCEPT {
return boost::stacktrace::detail::suppress_noinline_warnings::safe_dump_to_impl(fd, skip, max_depth);
}
#else
// POSIX
BOOST_FORCEINLINE std::size_t safe_dump_to(int fd) BOOST_NOEXCEPT {
return boost::stacktrace::detail::suppress_noinline_warnings::safe_dump_to_impl(fd, 0, boost::stacktrace::detail::max_frames_dump);
}
BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, std::size_t max_depth, int fd) BOOST_NOEXCEPT {
return boost::stacktrace::detail::suppress_noinline_warnings::safe_dump_to_impl(fd, skip, max_depth);
}
#endif
}} // namespace boost::stacktrace
#endif // BOOST_STACKTRACE_SAFE_DUMP_TO_HPP

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -16,74 +16,183 @@
#include <iosfwd>
#include <string>
#include <boost/container/vector.hpp>
#include <boost/stacktrace/stacktrace_fwd.hpp>
#include <boost/stacktrace/detail/backend.hpp>
#include <boost/stacktrace/frame.hpp>
#include <boost/stacktrace/const_iterator.hpp>
/// @cond
namespace boost {
// Forward declaration
template <class It> std::size_t hash_range(It, It);
}
/// @endcond
namespace boost { namespace stacktrace {
/// Class that on construction copies minimal information about call stack into its internals and provides access to that information.
/// @tparam Depth Max stack frames count that this class may hold. Equal to basic_stacktrace::max_depth.
template <std::size_t Depth>
/// @tparam Allocator Allocator to use during stack capture.
template <class Allocator>
class basic_stacktrace {
boost::container::vector<frame, Allocator> impl_;
/// @cond
void* impl_[Depth ? Depth : 1];
boost::stacktrace::detail::backend back_;
void fill(void** begin, std::size_t size) {
if (!size) {
return;
}
impl_.reserve(static_cast<std::size_t>(size));
for (std::size_t i = 0; i < size; ++i) {
if (!begin[i]) {
return;
}
impl_.push_back(
frame(begin[i])
);
}
}
static std::size_t frames_count_from_buffer_size(std::size_t buffer_size) BOOST_NOEXCEPT {
const std::size_t ret = (buffer_size > sizeof(void*) ? buffer_size / sizeof(void*) : 0);
return (ret > 1024 ? 1024 : ret); // Dealing with suspiciously big sizes
}
BOOST_NOINLINE void init(std::size_t frames_to_skip, std::size_t max_depth) {
BOOST_CONSTEXPR_OR_CONST std::size_t buffer_size = 128;
if (!max_depth) {
return;
}
try {
{ // Fast path without additional allocations
void* buffer[buffer_size];
const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(buffer, buffer_size, frames_to_skip + 1);
if (buffer_size > frames_count || frames_count >= max_depth) {
const std::size_t size = (max_depth < frames_count ? max_depth : frames_count);
fill(buffer, size);
return;
}
}
// Failed to fit in `buffer_size`. Allocating memory:
typedef typename Allocator::template rebind<void*>::other allocator_void_t;
boost::container::vector<void*, allocator_void_t> buf(buffer_size * 2, 0, impl_.get_allocator());
do {
const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(buf.data(), buf.size(), frames_to_skip + 1);
if (buf.size() > frames_count || frames_count >= max_depth) {
const std::size_t size = (max_depth < frames_count ? max_depth : frames_count);
fill(buf.data(), size);
return;
}
buf.resize(buf.size() * 2);
} while (1);
} catch (...) {
// ignore exception
}
}
/// @endcond
public:
/// Max stack frames count that this class may hold. Equal to Depth template parameter.
BOOST_STATIC_CONSTEXPR std::size_t max_depth = Depth;
typedef typename boost::container::vector<frame, Allocator>::value_type value_type;
typedef typename boost::container::vector<frame, Allocator>::allocator_type allocator_type;
typedef typename boost::container::vector<frame, Allocator>::const_pointer pointer;
typedef typename boost::container::vector<frame, Allocator>::const_pointer const_pointer;
typedef typename boost::container::vector<frame, Allocator>::const_reference reference;
typedef typename boost::container::vector<frame, Allocator>::const_reference const_reference;
typedef typename boost::container::vector<frame, Allocator>::size_type size_type;
typedef typename boost::container::vector<frame, Allocator>::difference_type difference_type;
typedef typename boost::container::vector<frame, Allocator>::const_iterator iterator;
typedef typename boost::container::vector<frame, Allocator>::const_iterator const_iterator;
typedef typename boost::container::vector<frame, Allocator>::const_reverse_iterator reverse_iterator;
typedef typename boost::container::vector<frame, Allocator>::const_reverse_iterator const_reverse_iterator;
typedef frame reference;
typedef boost::stacktrace::const_iterator iterator;
typedef boost::stacktrace::const_iterator const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
/// @brief Stores the current function call sequence inside the class.
/// @brief Stores the current function call sequence inside *this without any decoding or any other heavy platform specific operations.
///
/// @b Complexity: O(N) where N is call sequence length, O(1) for noop backend.
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Depends on backend, see "Build, Macros and Backends" section.
/// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
BOOST_FORCEINLINE basic_stacktrace() BOOST_NOEXCEPT
: impl_()
, back_(impl_, Depth)
{
init(0 , static_cast<std::size_t>(-1));
}
/// @brief Stores the current function call sequence inside *this without any decoding or any other heavy platform specific operations.
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
///
/// @param a Allocator that would be passed to underlying storeage.
BOOST_FORCEINLINE explicit basic_stacktrace(const allocator_type& a) BOOST_NOEXCEPT
: impl_(a)
{
init(0 , static_cast<std::size_t>(-1));
}
/// @brief Stores [skip, skip + max_depth) of the current function call sequence inside *this without any decoding or any other heavy platform specific operations.
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
///
/// @param skip How many top calls to skip and do not store in *this.
///
/// @param max_depth Max call sequence depth to collect.
///
/// @param a Allocator that would be passed to underlying storeage.
///
/// @throws Nothing. Note that default construction of allocator may throw, however it is
/// performed outside the constructor and exception in `allocator_type()` would not result in calling `std::terminate`.
BOOST_FORCEINLINE basic_stacktrace(std::size_t skip, std::size_t max_depth, const allocator_type& a = allocator_type()) BOOST_NOEXCEPT
: impl_(a)
{
init(skip , max_depth);
}
/// @b Complexity: O(st.size())
///
/// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
basic_stacktrace(const basic_stacktrace& st)
: impl_(st.impl_)
{}
/// @b Complexity: O(st.size())
///
/// @b Async-Handler-Safety: Safe.
basic_stacktrace(const basic_stacktrace& st) BOOST_NOEXCEPT
: impl_()
, back_(st.back_, impl_)
{}
/// @b Complexity: O(st.size())
///
/// @b Async-Handler-Safety: Safe.
basic_stacktrace& operator=(const basic_stacktrace& st) BOOST_NOEXCEPT {
back_ = st.back_;
/// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
basic_stacktrace& operator=(const basic_stacktrace& st) {
impl_ = st.impl_;
return *this;
}
#ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
~basic_stacktrace() BOOST_NOEXCEPT {}
/// @b Async-Handler-Safety: Safe if Allocator::deallocate is async signal safe.
~basic_stacktrace() BOOST_NOEXCEPT = default;
#endif
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS)
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe if Allocator construction and copying are async signal safe.
basic_stacktrace(basic_stacktrace&& st) = default;
/// @b Complexity: O(st.size())
///
/// @b Async-Handler-Safety: Safe if Allocator construction and copying are async signal safe.
basic_stacktrace& operator=(basic_stacktrace&& st) = default;
#endif
/// @returns Number of function names stored inside the class.
///
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
std::size_t size() const BOOST_NOEXCEPT {
return back_.size();
size_type size() const BOOST_NOEXCEPT {
return impl_.size();
}
/// @param frame_no Zero based index of frame to return. 0
@@ -91,47 +200,46 @@ public:
/// index close to this->size() contains function `main()`.
/// @returns frame that references the actual frame info, stored inside *this.
///
/// @b Complexity: Amortized O(1), O(1) for noop backend.
/// @b Complexity: O(1).
///
/// @b Async-Handler-Safety: Safe.
frame operator[](std::size_t frame_no) const BOOST_NOEXCEPT {
return *(cbegin() + frame_no);
const_reference operator[](std::size_t frame_no) const BOOST_NOEXCEPT {
return impl_[frame_no];
}
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
const_iterator begin() const BOOST_NOEXCEPT { return impl_.begin(); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
const_iterator cbegin() const BOOST_NOEXCEPT { return impl_.begin(); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
const_iterator end() const BOOST_NOEXCEPT { return impl_.end(); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
const_iterator cend() const BOOST_NOEXCEPT { return impl_.end(); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
const_iterator begin() const BOOST_NOEXCEPT { return const_iterator(&back_, 0); }
const_reverse_iterator rbegin() const BOOST_NOEXCEPT { return impl_.rbegin(); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
const_iterator cbegin() const BOOST_NOEXCEPT { return const_iterator(&back_, 0); }
const_reverse_iterator crbegin() const BOOST_NOEXCEPT { return impl_.rbegin(); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
const_iterator end() const BOOST_NOEXCEPT { return const_iterator(&back_, size()); }
const_reverse_iterator rend() const BOOST_NOEXCEPT { return impl_.rend(); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
const_iterator cend() const BOOST_NOEXCEPT { return const_iterator(&back_, size()); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
const_reverse_iterator rbegin() const BOOST_NOEXCEPT { return const_reverse_iterator( const_iterator(&back_, 0) ); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
const_reverse_iterator crbegin() const BOOST_NOEXCEPT { return const_reverse_iterator( const_iterator(&back_, 0) ); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
const_reverse_iterator rend() const BOOST_NOEXCEPT { return const_reverse_iterator( const_iterator(&back_, size()) ); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
const_reverse_iterator crend() const BOOST_NOEXCEPT { return const_reverse_iterator( const_iterator(&back_, size()) ); }
const_reverse_iterator crend() const BOOST_NOEXCEPT { return impl_.rend(); }
/// @brief Allows to check that stack trace capturing was successful.
@@ -142,7 +250,6 @@ public:
/// @b Async-Handler-Safety: Safe.
BOOST_EXPLICIT_OPERATOR_BOOL_NOEXCEPT()
/// @brief Allows to check that stack trace failed.
/// @returns `true` if `this->size() == 0`
///
@@ -151,82 +258,126 @@ public:
/// @b Async-Handler-Safety: Safe.
bool empty() const BOOST_NOEXCEPT { return !size(); }
/// @brief Compares stacktraces for less, order is platform dependant.
///
/// @b Complexity: Amortized O(1); worst case O(size())
///
/// @b Async-Handler-Safety: Safe.
bool operator< (const basic_stacktrace& rhs) const BOOST_NOEXCEPT {
return back_ < rhs.back_;
}
/// @brief Compares stacktraces for equality.
///
/// @b Complexity: Amortized O(1); worst case O(size())
///
/// @b Async-Handler-Safety: Safe.
bool operator==(const basic_stacktrace& rhs) const BOOST_NOEXCEPT {
return back_ == rhs.back_;
}
/// @brief Returns hashed code of the stacktrace.
///
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
std::size_t hash_code() const BOOST_NOEXCEPT { return back_.hash_code(); }
/// @cond
bool operator!() const BOOST_NOEXCEPT { return !size(); }
/// @endcond
const boost::container::vector<frame, Allocator>& as_vector() const BOOST_NOEXCEPT {
return impl_;
}
/// Constructs stacktrace from basic_istreamable that references the dumped stacktrace.
///
/// @b Complexity: O(N)
template <class Char, class Trait>
static basic_stacktrace from_dump(std::basic_istream<Char, Trait>& in, const allocator_type& a = allocator_type()) {
typedef typename std::basic_istream<Char, Trait>::pos_type pos_type;
basic_stacktrace ret(0, 0, a);
// reserving space
const pos_type pos = in.tellg();
in.seekg(0, in.end);
const std::size_t frames_count = frames_count_from_buffer_size(static_cast<std::size_t>(in.tellg()));
in.seekg(pos);
if (!frames_count) {
return ret;
}
void* ptr = 0;
ret.impl_.reserve(frames_count);
while (in.read(reinterpret_cast<Char*>(&ptr), sizeof(ptr))) {
if (!ptr) {
break;
}
ret.impl_.emplace_back(ptr);
}
return ret;
}
/// Constructs stacktrace from raw memory dump.
///
/// @b Complexity: O(size) in worst case
static basic_stacktrace from_dump(const void* begin, std::size_t size, const allocator_type& a = allocator_type()) {
basic_stacktrace ret(0, 0, a);
const void* const* first = static_cast<const void* const*>(begin);
const std::size_t frames_count = frames_count_from_buffer_size(size);
if (!frames_count) {
return ret;
}
const void* const* const last = first + frames_count;
ret.impl_.reserve(frames_count);
for (; first != last; ++first) {
if (!*first) {
break;
}
ret.impl_.emplace_back(*first);
}
return ret;
}
};
/// @brief Compares stacktraces for less, order is platform dependent.
///
/// @b Complexity: Amortized O(1); worst case O(size())
///
/// @b Async-Handler-Safety: Safe.
template <class Allocator1, class Allocator2>
bool operator< (const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT {
return lhs.size() < rhs.size() || (lhs.size() == rhs.size() && lhs.as_vector() < rhs.as_vector());
}
/// @brief Compares stacktraces for equality.
///
/// @b Complexity: Amortized O(1); worst case O(size())
///
/// @b Async-Handler-Safety: Safe.
template <class Allocator1, class Allocator2>
bool operator==(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT {
return lhs.as_vector() == rhs.as_vector();
}
/// Comparison operators that provide platform dependant ordering and have amortized O(1) complexity; O(size()) worst case complexity; are Async-Handler-Safe.
template <std::size_t Depth>
bool operator> (const basic_stacktrace<Depth>& lhs, const basic_stacktrace<Depth>& rhs) BOOST_NOEXCEPT {
template <class Allocator1, class Allocator2>
bool operator> (const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT {
return rhs < lhs;
}
template <std::size_t Depth>
bool operator<=(const basic_stacktrace<Depth>& lhs, const basic_stacktrace<Depth>& rhs) BOOST_NOEXCEPT {
template <class Allocator1, class Allocator2>
bool operator<=(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT {
return !(lhs > rhs);
}
template <std::size_t Depth>
bool operator>=(const basic_stacktrace<Depth>& lhs, const basic_stacktrace<Depth>& rhs) BOOST_NOEXCEPT {
template <class Allocator1, class Allocator2>
bool operator>=(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT {
return !(lhs < rhs);
}
template <std::size_t Depth>
bool operator!=(const basic_stacktrace<Depth>& lhs, const basic_stacktrace<Depth>& rhs) BOOST_NOEXCEPT {
template <class Allocator1, class Allocator2>
bool operator!=(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT {
return !(lhs == rhs);
}
/// Hashing support, O(1) complexity; Async-Handler-Safe.
template <std::size_t Depth>
std::size_t hash_value(const basic_stacktrace<Depth>& st) BOOST_NOEXCEPT {
return st.hash_code();
/// Fast hashing support, O(st.size()) complexity; Async-Handler-Safe.
template <class Allocator>
std::size_t hash_value(const basic_stacktrace<Allocator>& st) BOOST_NOEXCEPT {
return boost::hash_range(st.as_vector().data(), st.as_vector().data()+ st.as_vector().size());
}
/// Outputs stacktrace in a human readable format to output stream; unsafe to use in async handlers.
template <class CharT, class TraitsT, std::size_t Depth>
std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const basic_stacktrace<Depth>& bt) {
const std::streamsize w = os.width();
const std::size_t frames = bt.size();
for (std::size_t i = 0; i < frames; ++i) {
os.width(2);
os << i;
os.width(w);
os << "# ";
os << bt[i];
os << '\n';
}
return os;
template <class CharT, class TraitsT, class Allocator>
std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const basic_stacktrace<Allocator>& bt) {
return os << boost::stacktrace::detail::to_string(bt.as_vector().data(), bt.size());
}
typedef basic_stacktrace<BOOST_STACKTRACE_DEFAULT_MAX_DEPTH> stacktrace;
/// This is the typedef to use unless you'd like to provide a specific allocator to boost::stacktrace::basic_stacktrace.
typedef basic_stacktrace<> stacktrace;
}} // namespace boost::stacktrace

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -8,32 +8,21 @@
#define BOOST_STACKTRACE_STACKTRACE_FWD_HPP
#include <cstddef>
#include <memory>
/// @file stacktrace_fwd.hpp This header contains only forward declarations of
/// boost::stacktrace::frame, boost::stacktrace::const_iterator, boost::stacktrace::basic_stacktrace
/// boost::stacktrace::frame, boost::stacktrace::basic_stacktrace, boost::stacktrace::stacktrace
/// and does not include any other Boost headers.
#ifndef BOOST_STACKTRACE_DEFAULT_MAX_DEPTH
/// You may define this macro to some positive integer to limit the max stack frames count for the boost::stacktrace::stacktrace class.
/// This macro does not affect the boost::stacktrace::basic_stacktrace.
///
/// @b Default: 100
#define BOOST_STACKTRACE_DEFAULT_MAX_DEPTH 100
#endif
/// @cond
namespace boost { namespace stacktrace {
class frame;
class const_iterator;
template <std::size_t Depth>
class basic_stacktrace;
typedef basic_stacktrace<BOOST_STACKTRACE_DEFAULT_MAX_DEPTH> stacktrace;
template <class Allocator = std::allocator<frame> > class basic_stacktrace;
typedef basic_stacktrace<> stacktrace;
}} // namespace boost::stacktrace
/// @endcond
#endif // BOOST_STACKTRACE_STACKTRACE_FWD_HPP

10
src/addr2line.cpp Normal file
View File

@@ -0,0 +1,10 @@
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#define BOOST_STACKTRACE_INTERNAL_BUILD_LIBS
#define BOOST_STACKTRACE_USE_ADDR2LINE
#define BOOST_STACKTRACE_LINK
#include <boost/stacktrace/detail/frame_unwind.ipp>

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -7,4 +7,4 @@
#define BOOST_STACKTRACE_INTERNAL_BUILD_LIBS
#define BOOST_STACKTRACE_USE_BACKTRACE
#define BOOST_STACKTRACE_LINK
#include <boost/stacktrace/detail/backend.ipp>
#include <boost/stacktrace/detail/frame_unwind.ipp>

View File

@@ -1,10 +1,9 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#define BOOST_STACKTRACE_INTERNAL_BUILD_LIBS
#define BOOST_STACKTRACE_USE_UNWIND
#define BOOST_STACKTRACE_LINK
#include <boost/stacktrace/detail/backend.ipp>
#include <boost/stacktrace/detail/frame_unwind.ipp>

View File

@@ -1,10 +1,9 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#define BOOST_STACKTRACE_INTERNAL_BUILD_LIBS
#define BOOST_STACKTRACE_USE_NOOP
#define BOOST_STACKTRACE_LINK
#include <boost/stacktrace/detail/backend.ipp>
#include <boost/stacktrace/detail/frame_noop.ipp>

View File

@@ -1,10 +1,9 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// 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)
#define BOOST_STACKTRACE_INTERNAL_BUILD_LIBS
#define BOOST_STACKTRACE_USE_WINDBG
#define BOOST_STACKTRACE_LINK
#include <boost/stacktrace/detail/backend.ipp>
#include <boost/stacktrace/detail/frame_msvc.ipp>

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2016, Antony Polukhin.
# Copyright (C) 2016-2017, Antony Polukhin.
#
# Use, modification and distribution is subject to the Boost Software License,
# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
@@ -7,7 +7,18 @@
lib dl : : <link>shared ;
lib gcc_s ;
lib Dbghelp ;
lib rt ;
lib Dbgeng ;
lib ole32 ;
local LIBBACKTRACE_PATH = [ modules.peek : LIBBACKTRACE_PATH ] ;
lib backtrace
:
: <search>$(LIBBACKTRACE_PATH)/lib <link>static
:
: <include>$(LIBBACKTRACE_PATH)/include
;
project
: requirements
@@ -15,50 +26,67 @@ project
<warnings>all
<debug-symbols>on
<test-info>always_show_run_output
<target-os>linux:<cxxflags>"-fvisibility=hidden"
;
local RDYNAMIC = <target-os>freebsd:<linkflags>"-rdynamic" <target-os>solaris:<linkflags>"-Bdynamic" <target-os>aix:<linkflags>"-rdynamic"
<target-os>qnxnto,<toolset>qcc:<linkflags>"-Bdynamic" <target-os>qnxnto,<toolset>gcc:<linkflags>"-rdynamic"
<target-os>android:<linkflags>"-rdynamic" <target-os>linux:<linkflags>"-rdynamic" <target-os>darwin,<toolset>gcc:<linkflags>"-dynamic"
<target-os>darwin,<toolset>clang:<linkflags>"-rdynamic" <target-os>iphone:<linkflags>"-rdynamic" ;
local BT_DEPS = <target-os>linux:<library>dl [ check-target-builds ../build//backtrace : : <build>no ] ;
local UNWD_DEPS = <target-os>linux:<library>dl [ check-target-builds ../build//unwind : : <build>no ] ;
local WIND_DEPS = <library>Dbghelp [ check-target-builds ../build//WinDbg : : <build>no ] ;
local NOOP_DEPS = ;
local AUTO_DEPS = <target-os>linux:<library>dl [ check-target-builds ../build//WinDbg : <library>Dbghelp ] ;
local HIDE_SYMBS = <target-os>linux:<cxxflags>"-fvisibility=hidden" ;
local LINKSHARED_BT = <link>shared <define>BOOST_STACKTRACE_DYN_LINK <library>/boost/stacktrace//boost_stacktrace_backtrace $(BT_DEPS) ;
local LINKSHARED_UNWD = <link>shared <define>BOOST_STACKTRACE_DYN_LINK <library>/boost/stacktrace//boost_stacktrace_unwind $(UNWD_DEPS) ;
local LINKSHARED_WIND = <link>shared <define>BOOST_STACKTRACE_DYN_LINK <library>/boost/stacktrace//boost_stacktrace_windbg $(WIND_DEPS) ;
local LINKSHARED_NOOP = <link>shared <define>BOOST_STACKTRACE_DYN_LINK <library>/boost/stacktrace//boost_stacktrace_noop $(NOOP_DEPS) ;
local BT_DEPS = $(HIDE_SYMBS) <target-os>linux:<library>dl <library>backtrace [ check-target-builds ../build//libbacktrace : : <build>no ] ;
local UNWD_DEPS = $(HIDE_SYMBS) <target-os>linux:<library>dl [ check-target-builds ../build//addr2line : : <build>no ] ;
local WIND_DEPS = $(HIDE_SYMBS) <library>Dbgeng <library>ole32 [ check-target-builds ../build//WinDbg : : <build>no ] ;
local NOOP_DEPS = $(HIDE_SYMBS) ;
local BASIC_DEPS = $(RDYNAMIC) <target-os>linux:<library>dl [ check-target-builds ../build//WinDbg : <build>no ] ;
local LINKSHARED_BT = <link>shared <define>BOOST_STACKTRACE_DYN_LINK <library>/boost/stacktrace//boost_stacktrace_backtrace $(BT_DEPS) ;
local LINKSHARED_AD2L = <link>shared <define>BOOST_STACKTRACE_DYN_LINK <library>/boost/stacktrace//boost_stacktrace_addr2line $(UNWD_DEPS) ;
local LINKSHARED_WIND = <link>shared <define>BOOST_STACKTRACE_DYN_LINK <library>/boost/stacktrace//boost_stacktrace_windbg $(WIND_DEPS) ;
local LINKSHARED_NOOP = <link>shared <define>BOOST_STACKTRACE_DYN_LINK <library>/boost/stacktrace//boost_stacktrace_noop $(NOOP_DEPS) ;
local LINKSHARED_BASIC = <link>shared <define>BOOST_STACKTRACE_DYN_LINK <library>/boost/stacktrace//boost_stacktrace_basic $(BASIC_DEPS) ;
lib test_impl_lib_backtrace : test_impl.cpp : $(LINKSHARED_BT) ;
lib test_impl_lib_unwind : test_impl.cpp : $(LINKSHARED_UNWD) ;
lib test_impl_lib_addr2line : test_impl.cpp : $(LINKSHARED_AD2L) ;
lib test_impl_lib_windbg : test_impl.cpp : $(LINKSHARED_WIND) ;
lib test_impl_lib_noop : test_impl.cpp : $(LINKSHARED_NOOP) ;
obj test_impl_nohide-obj : test_impl.cpp : $(LINKSHARED_BASIC) ;
lib test_impl_lib_basic : test_impl_nohide-obj : $(LINKSHARED_BASIC) ;
test-suite stacktrace_tests
:
# Header only tests
[ run test.cpp test_impl.cpp : : : <define>BOOST_STACKTRACE_USE_BACKTRACE $(BT_DEPS) : backtrace_ho ]
[ run test.cpp test_impl.cpp : : : <define>BOOST_STACKTRACE_USE_UNWIND $(UNWD_DEPS) : unwind_ho ]
[ run test.cpp test_impl.cpp : : : <define>BOOST_STACKTRACE_USE_WINDBG $(WIND_DEPS) : windbg_ho ]
[ run test_noop.cpp test_impl.cpp : : : <define>BOOST_STACKTRACE_USE_NOOP $(NOOP_DEPS) : noop_ho ]
[ run test.cpp test_impl.cpp : : : $(AUTO_DEPS) : autodetect_ho ]
[ run test.cpp test_impl.cpp : : : <define>BOOST_STACKTRACE_USE_BACKTRACE $(BT_DEPS) : backtrace_ho ]
[ run test.cpp test_impl.cpp : : : <define>BOOST_STACKTRACE_USE_ADDR2LINE $(UNWD_DEPS) : addr2line_ho ]
[ run test_noop.cpp test_impl.cpp : : : <define>BOOST_STACKTRACE_USE_NOOP $(NOOP_DEPS) : noop_ho ]
[ run test.cpp test_impl.cpp : : : $(WIND_DEPS) : windbg_ho ]
[ run test.cpp test_impl.cpp : : : $(BASIC_DEPS) : basic_ho ]
# Test with shared linked backends
[ run test.cpp : : : <library>.//test_impl_lib_backtrace $(LINKSHARED_BT) : backtrace_lib ]
[ run test.cpp : : : <library>.//test_impl_lib_unwind $(LINKSHARED_UNWD) : unwind_lib ]
[ run test.cpp : : : <library>.//test_impl_lib_windbg $(LINKSHARED_WIND) : windbg_lib ]
[ run test_noop.cpp : : : <library>.//test_impl_lib_noop $(LINKSHARED_NOOP) : noop_lib ]
# Test with shared linked implementations
[ run test.cpp : : : <library>.//test_impl_lib_backtrace $(LINKSHARED_BT) : backtrace_lib ]
[ run test.cpp : : : <library>.//test_impl_lib_addr2line $(LINKSHARED_AD2L) : addr2line_lib ]
[ run test.cpp : : : <library>.//test_impl_lib_windbg $(LINKSHARED_WIND) : windbg_lib ]
[ run test_noop.cpp : : : <library>.//test_impl_lib_noop $(LINKSHARED_NOOP) : noop_lib ]
[ run test.cpp : : : <library>.//test_impl_lib_basic $(LINKSHARED_BASIC) : basic_lib ]
;
# Assuring that examples compile and run. Adding sources from `examples` directory to the `type_index` test suite.
for local p in [ glob ../example/*.cpp ]
{
stacktrace_tests += [ run $(p) : : : $(LINKSHARED_BT) : backtrace_$(p[1]:B) ] ;
stacktrace_tests += [ run $(p) : : : $(LINKSHARED_UNWD) : unwind_$(p[1]:B) ] ;
stacktrace_tests += [ run $(p) : : : $(LINKSHARED_WIND) : windbg_$(p[1]:B) ] ;
stacktrace_tests += [ run $(p) : : : $(LINKSHARED_NOOP) : noop_$(p[1]:B) ] ;
stacktrace_tests += [ run $(p) : : : $(AUTO_DEPS) : autodetect_$(p[1]:B) ] ;
local target_name = $(p[1]:B) ;
local additional_dependency = ;
if $(target_name) = "terminate_handler"
{
additional_dependency = <library>/boost/filesystem//boost_filesystem <library>/boost/system//boost_system <target-os>linux:<library>rt ;
}
stacktrace_tests += [ run $(p) : : : $(LINKSHARED_BT) $(additional_dependency) : backtrace_$(p[1]:B) ] ;
stacktrace_tests += [ run $(p) : : : $(LINKSHARED_AD2L) $(additional_dependency) : addr2line_$(p[1]:B) ] ;
stacktrace_tests += [ run $(p) : : : $(LINKSHARED_WIND) $(additional_dependency) : windbg_$(p[1]:B) ] ;
stacktrace_tests += [ run $(p) : : : $(LINKSHARED_NOOP) $(additional_dependency) : noop_$(p[1]:B) ] ;
stacktrace_tests += [ run $(p) : : : $(LINKSHARED_BASIC) $(additional_dependency) : basic_$(p[1]:B) ] ;
}

View File

@@ -2,27 +2,23 @@
# subject to 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)
#
# Copyright Antony Polukhin 2016.
# Copyright Antony Polukhin 2016-2017.
#
# See https://svn.boost.org/trac/boost/wiki/TravisCoverals for description of this file
# and how it can be used with Boost libraries.
#
# File revision #2
# File revision #3
init:
- set BRANCH_TO_TEST=%APPVEYOR_REPO_BRANCH%
- set BOOST_REMOVE=stacktrace
os: Visual Studio 2015
configuration: Debug
platform: x64
- set BRANCH_TO_TEST=%APPVEYOR_REPO_BRANCH% # Change to branch you wish to test. Use %APPVEYOR_REPO_BRANCH% for current branch.
- set BOOST_REMOVE=stacktrace # Remove this folder from lib from full clone of Boost. If you are testing `any` repo, write here `any`.
###############################################################################################################
# From this point and below code is same for all the Boost libs
###############################################################################################################
version: 1.61.{build}-{branch}
version: 1.64.{build}-{branch}
# branches to build
branches:
@@ -32,6 +28,7 @@ branches:
skip_tags: true
before_build:
- set PATH=%PATH%;C:\\MinGW\\bin
# Set this to the name of the library
- set PROJECT_TO_TEST=%APPVEYOR_PROJECT_NAME%
- echo "Testing %PROJECT_TO_TEST%"
@@ -59,7 +56,7 @@ build_script:
after_build:
before_test:
test_script:
- ..\..\..\b2.exe address-model=64 architecture=x86 cxxflags="-DBOOST_TRAVISCI_BUILD" -sBOOST_BUILD_PATH=.
- ..\..\..\b2.exe address-model=32 architecture=x86 toolset=msvc,gcc cxxflags="-DBOOST_TRAVISCI_BUILD" -sBOOST_BUILD_PATH=.
after_test:
on_success:

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -12,6 +12,9 @@
#include <sstream>
#include <boost/core/lightweight_test.hpp>
#include <boost/functional/hash.hpp>
using boost::stacktrace::stacktrace;
using boost::stacktrace::frame;
@@ -21,30 +24,53 @@ using boost::stacktrace::frame;
# define BOOST_ST_API
#endif
BOOST_ST_API std::pair<stacktrace, stacktrace> foo2(int i);
BOOST_ST_API std::pair<stacktrace, stacktrace> foo1(int i);
#if defined(BOOST_GCC) && defined(BOOST_WINDOWS) && !defined(BOOST_STACKTRACE_USE_BACKTRACE) && !defined(BOOST_STACKTRACE_USE_ADDR2LINE)
// MinGW with basic functionality
# define BOOST_STACKTRACE_SYMNAME 0
#else
# define BOOST_STACKTRACE_SYMNAME 1
#endif
typedef std::pair<stacktrace, stacktrace> (*foo1_t)(int i);
BOOST_ST_API std::pair<stacktrace, stacktrace> foo2(int i, foo1_t foo1);
BOOST_ST_API stacktrace return_from_nested_namespaces();
BOOST_ST_API boost::stacktrace::basic_stacktrace<4> bar1();
BOOST_ST_API boost::stacktrace::basic_stacktrace<4> bar2();
BOOST_ST_API boost::stacktrace::stacktrace bar1();
BOOST_ST_API boost::stacktrace::stacktrace bar2();
BOOST_NOINLINE std::pair<stacktrace, stacktrace> foo1(int i) {
if (i) {
return foo2(i - 1, foo1);
}
std::pair<stacktrace, stacktrace> ret;
try {
throw std::logic_error("test");
} catch (const std::logic_error& /*e*/) {
ret.second = stacktrace();
return ret;
}
}
void test_deeply_nested_namespaces() {
std::stringstream ss;
ss << return_from_nested_namespaces();
std::cout << ss.str() << '\n';
#if BOOST_STACKTRACE_SYMNAME
BOOST_TEST(ss.str().find("main") != std::string::npos);
#if defined(BOOST_STACKTRACE_DYN_LINK)
BOOST_TEST(ss.str().find("get_backtrace_from_nested_namespaces") != std::string::npos
|| ss.str().find("return_from_nested_namespaces") != std::string::npos);
BOOST_TEST(ss.str().find("get_backtrace_from_nested_namespaces") != std::string::npos);
BOOST_TEST(ss.str().find("return_from_nested_namespaces") != std::string::npos);
#endif
stacktrace ns1 = return_from_nested_namespaces();
BOOST_TEST(ns1 != return_from_nested_namespaces()); // Different addresses in test_deeply_nested_namespaces() function
}
// Template parameter Depth is to produce different functions on each Depth. This simplifies debugging when one of the tests catches error
template <std::size_t Depth>
void test_nested() {
std::pair<stacktrace, stacktrace> res = foo2(15);
std::pair<stacktrace, stacktrace> res = foo2(Depth, foo1);
std::stringstream ss1, ss2;
@@ -59,16 +85,21 @@ void test_nested() {
BOOST_TEST(ss1.str().find(" 1# ") != std::string::npos);
BOOST_TEST(ss2.str().find(" 1# ") != std::string::npos);
BOOST_TEST(ss1.str().find(" in ") != std::string::npos);
BOOST_TEST(ss2.str().find(" in ") != std::string::npos);
#if BOOST_STACKTRACE_SYMNAME
BOOST_TEST(ss1.str().find("main") != std::string::npos);
BOOST_TEST(ss2.str().find("main") != std::string::npos);
#if defined(BOOST_STACKTRACE_DYN_LINK) || !defined(BOOST_STACKTRACE_USE_BACKTRACE)
BOOST_TEST(ss1.str().find("foo1") != std::string::npos);
BOOST_TEST(ss1.str().find("foo2") != std::string::npos);
BOOST_TEST(ss2.str().find("foo1") != std::string::npos);
BOOST_TEST(ss2.str().find("foo2") != std::string::npos);
BOOST_TEST(ss1.str().find("foo1") != std::string::npos);
BOOST_TEST(ss2.str().find("foo1") != std::string::npos);
#endif
//BOOST_TEST(false);
}
@@ -79,6 +110,7 @@ void test_comparisons_base(Bt nst, Bt st) {
cst = cst;
BOOST_TEST(nst);
BOOST_TEST(st);
BOOST_TEST(nst[0] != st[0]);
BOOST_TEST(nst != st);
BOOST_TEST(st != nst);
@@ -165,10 +197,10 @@ void test_iterators() {
BOOST_TEST(st.size() == static_cast<std::size_t>(st.cend() - st.cbegin()));
BOOST_TEST(st.size() == static_cast<std::size_t>(st.cend() - st.begin()));
BOOST_TEST(st.size() == static_cast<std::size_t>(std::distance(st.rend(), st.rbegin())));
BOOST_TEST(st.size() == static_cast<std::size_t>(std::distance(st.rend(), st.crbegin())));
BOOST_TEST(st.size() == static_cast<std::size_t>(std::distance(st.crend(), st.crbegin())));
BOOST_TEST(st.size() == static_cast<std::size_t>(std::distance(st.crend(), st.rbegin())));
BOOST_TEST(st.size() == static_cast<std::size_t>(std::distance(st.rbegin(), st.rend())));
BOOST_TEST(st.size() == static_cast<std::size_t>(std::distance(st.crbegin(), st.rend())));
BOOST_TEST(st.size() == static_cast<std::size_t>(std::distance(st.crbegin(), st.crend())));
BOOST_TEST(st.size() == static_cast<std::size_t>(std::distance(st.rbegin(), st.crend())));
boost::stacktrace::stacktrace::iterator it = st.begin();
@@ -190,15 +222,17 @@ void test_frame() {
BOOST_TEST(st[i] <= st[i]);
BOOST_TEST(st[i] >= st[i]);
frame fv = nst[2];
frame fv = nst[i];
BOOST_TEST(fv);
if (i >= 2 && i < min_size - 3) { // Begin and end of the trace may match, skipping them
if (i > 1 && i < min_size - 3) { // Begin ...and end of the trace may match, skipping
BOOST_TEST(st[i] != fv);
BOOST_TEST(st[i].name() != fv.name());
BOOST_TEST(st[i] != fv);
BOOST_TEST(st[i] < fv || st[i] > fv);
BOOST_TEST(hash_value(st[i]) != hash_value(fv));
BOOST_TEST(st[i].source_line() == 0 || st[i].source_file() != fv.source_file());
if (st[i].source_line()) {
BOOST_TEST(st[i].source_file() != fv.source_file() || st[i].source_line() != fv.source_line());
}
BOOST_TEST(st[i]);
}
@@ -213,9 +247,11 @@ void test_frame() {
BOOST_TEST(empty_frame.source_line() == 0);
}
// Template parameter bool BySkip is to produce different functions on each BySkip. This simplifies debugging when one of the tests catches error
template <bool BySkip>
void test_empty_basic_stacktrace() {
typedef boost::stacktrace::basic_stacktrace<0> st_t;
st_t st;
typedef boost::stacktrace::stacktrace st_t;
st_t st = BySkip ? st_t(100500, 1024) : st_t(0, 0);
BOOST_TEST(!st);
BOOST_TEST(st.empty());
@@ -230,26 +266,32 @@ void test_empty_basic_stacktrace() {
BOOST_TEST(st.crbegin() == st.crend());
BOOST_TEST(st.rbegin() == st.crend());
BOOST_TEST(hash_value(st) == hash_value(st_t()));
BOOST_TEST(st == st_t());
BOOST_TEST(!(st < st_t()));
BOOST_TEST(!(st > st_t()));
BOOST_TEST(hash_value(st) == hash_value(st_t(0, 0)));
BOOST_TEST(st == st_t(0, 0));
BOOST_TEST(!(st < st_t(0, 0)));
BOOST_TEST(!(st > st_t(0, 0)));
}
int main() {
test_deeply_nested_namespaces();
test_nested();
test_nested<15>();
test_comparisons();
test_iterators();
test_frame();
test_empty_basic_stacktrace();
test_empty_basic_stacktrace<true>();
test_empty_basic_stacktrace<false>();
BOOST_TEST(&bar1 != &bar2);
boost::stacktrace::basic_stacktrace<4> b1 = bar1();
boost::stacktrace::stacktrace b1 = bar1();
BOOST_TEST(b1.size() == 4);
boost::stacktrace::basic_stacktrace<4> b2 = bar2();
boost::stacktrace::stacktrace b2 = bar2();
BOOST_TEST(b2.size() == 4);
test_comparisons_base(bar1(), bar2());
test_nested<250>();
test_nested<300>();
BOOST_TEST(boost::stacktrace::stacktrace(0, 1).size() == 1);
BOOST_TEST(boost::stacktrace::stacktrace(1, 1).size() == 1);
return boost::report_errors();
}

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -16,24 +16,8 @@ using namespace boost::stacktrace;
# define BOOST_ST_API
#endif
BOOST_ST_API BOOST_NOINLINE std::pair<stacktrace, stacktrace> foo1(int i);
BOOST_ST_API BOOST_NOINLINE std::pair<stacktrace, stacktrace> foo2(int i);
std::pair<stacktrace, stacktrace> foo1(int i) {
if (i) {
return foo2(i - 1);
}
std::pair<stacktrace, stacktrace> ret;
try {
throw std::logic_error("test");
} catch (const std::logic_error& /*e*/) {
ret.second = stacktrace();
return ret;
}
}
std::pair<stacktrace, stacktrace> foo2(int i) {
typedef std::pair<stacktrace, stacktrace> (*foo1_t)(int i);
BOOST_ST_API BOOST_NOINLINE std::pair<stacktrace, stacktrace> foo2(int i, foo1_t foo1) {
if (i) {
return foo1(--i);
} else {
@@ -66,13 +50,31 @@ BOOST_ST_API BOOST_NOINLINE stacktrace return_from_nested_namespaces() {
return get_backtrace_from_nested_namespaces();
}
BOOST_ST_API BOOST_NOINLINE boost::stacktrace::basic_stacktrace<4> bar1() {
boost::stacktrace::basic_stacktrace<4> result;
BOOST_ST_API BOOST_NOINLINE boost::stacktrace::stacktrace bar1_impl(int d = 0) {
boost::stacktrace::stacktrace result(0, 4);
if (result.size() < 4) {
if (d > 4) throw std::runtime_error("Stack is not growing in test OR stacktrace fails to work in `bar1` function.");
return bar1_impl(d + 1);
}
return result;
}
BOOST_ST_API BOOST_NOINLINE boost::stacktrace::basic_stacktrace<4> bar2() {
boost::stacktrace::basic_stacktrace<4> result;
BOOST_ST_API BOOST_NOINLINE boost::stacktrace::stacktrace bar2_impl(int d = 0) {
boost::stacktrace::stacktrace result(0, 4);
if (result.size() < 4) {
if (d > 4) throw std::runtime_error("Stack is not growing in test OR stacktrace fails to work in `bar2` function.");
return bar2_impl(d + 1);
}
return result;
}
BOOST_ST_API BOOST_NOINLINE boost::stacktrace::stacktrace bar1() {
boost::stacktrace::stacktrace result = bar1_impl();
return result;
}
BOOST_ST_API BOOST_NOINLINE boost::stacktrace::stacktrace bar2() {
boost::stacktrace::stacktrace result = bar2_impl();
return result;
}

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016.
// Copyright Antony Polukhin, 2016-2017.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -8,34 +8,48 @@
#include <boost/stacktrace.hpp>
#include <boost/core/lightweight_test.hpp>
#include <boost/functional/hash.hpp>
using boost::stacktrace::stacktrace;
BOOST_SYMBOL_IMPORT std::pair<stacktrace, stacktrace> foo2(int i);
BOOST_SYMBOL_IMPORT std::pair<stacktrace, stacktrace> foo1(int i);
BOOST_SYMBOL_IMPORT stacktrace return_from_nested_namespaces();
using boost::stacktrace::frame;
#ifdef BOOST_STACKTRACE_DYN_LINK
# define BOOST_ST_API BOOST_SYMBOL_IMPORT
#else
# define BOOST_ST_API
#endif
typedef std::pair<stacktrace, stacktrace> (*foo1_t)(int i);
BOOST_ST_API std::pair<stacktrace, stacktrace> foo2(int i, foo1_t foo1);
BOOST_ST_API stacktrace return_from_nested_namespaces();
BOOST_NOINLINE std::pair<stacktrace, stacktrace> foo1(int i) {
if (i) {
return foo2(i - 1, foo1);
}
std::pair<stacktrace, stacktrace> ret;
try {
throw std::logic_error("test");
} catch (const std::logic_error& /*e*/) {
ret.second = stacktrace();
return ret;
}
}
void test_deeply_nested_namespaces() {
BOOST_TEST(return_from_nested_namespaces().size() == 0);
BOOST_TEST(return_from_nested_namespaces().begin()->name() == "");
BOOST_TEST(return_from_nested_namespaces().empty());
BOOST_TEST(!return_from_nested_namespaces());
}
void test_nested() {
std::pair<stacktrace, stacktrace> res = foo2(15);
std::pair<stacktrace, stacktrace> res = foo2(15, foo1);
BOOST_TEST(!res.first);
BOOST_TEST(res.first.empty());
BOOST_TEST(res.first.size() == 0);
BOOST_TEST(res.first.begin()->name() == "");
BOOST_TEST(res.first.begin()->source_file() == "");
BOOST_TEST(res.first.begin()->source_line() == 0);
BOOST_TEST(!res.second);
BOOST_TEST(res.second.size() == 0);
BOOST_TEST(res.second.begin()->name() == "");
BOOST_TEST(res.second.begin()->source_file() == "");
BOOST_TEST(res.second.begin()->source_line() == 0);
BOOST_TEST(res.second[0].name() == "");
BOOST_TEST(res.second[0].source_file() == "");
BOOST_TEST(res.second[0].source_line() == 0);
BOOST_TEST(res.second[0].address() == 0);
BOOST_TEST(res.second <= res.first);
BOOST_TEST(res.second >= res.first);
@@ -50,6 +64,11 @@ void test_empty_frame() {
BOOST_TEST(empty_frame.source_file() == "");
BOOST_TEST(empty_frame.name() == "");
BOOST_TEST(empty_frame.source_line() == 0);
boost::stacktrace::frame f(0);
BOOST_TEST(f.name() == "");
BOOST_TEST(f.source_file() == "");
BOOST_TEST(f.source_line() == 0);
}
int main() {