mirror of
https://github.com/boostorg/stacktrace.git
synced 2026-01-29 20:12:08 +00:00
Compare commits
83 Commits
boost_revi
...
boost_revi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f68fe4e9a | ||
|
|
b6fad0f575 | ||
|
|
5552c97150 | ||
|
|
3f48887f2e | ||
|
|
cdf7ef74e7 | ||
|
|
affbfef4b6 | ||
|
|
e420e0a071 | ||
|
|
34ff2dfc04 | ||
|
|
7f45997fb2 | ||
|
|
abd8afb6ec | ||
|
|
876b68d81c | ||
|
|
6ddf49e565 | ||
|
|
53d6ce5987 | ||
|
|
1c6fa29299 | ||
|
|
4191419560 | ||
|
|
7b6a8e84f7 | ||
|
|
94f73fe616 | ||
|
|
ca6fc8b312 | ||
|
|
b0e04ac94d | ||
|
|
d92c405ec1 | ||
|
|
6a146fe7e3 | ||
|
|
672a1dcbe3 | ||
|
|
00a13698e0 | ||
|
|
116dd872b8 | ||
|
|
eb9fcf8050 | ||
|
|
a246a5c148 | ||
|
|
675ab7d65d | ||
|
|
441d38af76 | ||
|
|
9c08e254f1 | ||
|
|
b842cb2284 | ||
|
|
8050e4ea8e | ||
|
|
ab6e88f1d0 | ||
|
|
270786eb1e | ||
|
|
ca0a912125 | ||
|
|
fd8c0d0bc3 | ||
|
|
925a90472e | ||
|
|
a462364409 | ||
|
|
224750cbef | ||
|
|
9146cedb94 | ||
|
|
34fdd6077e | ||
|
|
0875781884 | ||
|
|
843c6e4291 | ||
|
|
ea6e7fabe9 | ||
|
|
d26dc67be6 | ||
|
|
d535c5a0b1 | ||
|
|
e4c3542c96 | ||
|
|
fc0063de37 | ||
|
|
bc5b4fad18 | ||
|
|
13fe06063b | ||
|
|
c9315559a3 | ||
|
|
c221f0c2dd | ||
|
|
1c5274f9fa | ||
|
|
bd616a7249 | ||
|
|
3ec1c49c3b | ||
|
|
c5843350f0 | ||
|
|
7a40dc90ae | ||
|
|
85c31f691d | ||
|
|
f37eea04fa | ||
|
|
b7bf4b5932 | ||
|
|
a2431640b9 | ||
|
|
34306df187 | ||
|
|
3f543731fa | ||
|
|
c45c8ff0e4 | ||
|
|
053b9f5606 | ||
|
|
6de0fe088b | ||
|
|
d6c1350952 | ||
|
|
36061d4ef8 | ||
|
|
7149a04002 | ||
|
|
898380d622 | ||
|
|
5a9ba3342d | ||
|
|
8f06ce9b3d | ||
|
|
0b7fae6e48 | ||
|
|
60ac93f79f | ||
|
|
16ef077fa4 | ||
|
|
2893578446 | ||
|
|
99e4b53742 | ||
|
|
ec7abcdf68 | ||
|
|
41ed839e49 | ||
|
|
9d1a2d652b | ||
|
|
b23664a769 | ||
|
|
b282f55342 | ||
|
|
dce55ef2ef | ||
|
|
fb5927eef8 |
@@ -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`.
|
||||
|
||||
@@ -7,6 +7,7 @@ Library for storing and printing backtraces.
|
||||
### Test results
|
||||
@ | Build | Tests coverage | More info
|
||||
----------------|-------------- | -------------- |-----------
|
||||
Develop branch: | [](https://travis-ci.org/apolukhin/stacktrace) [](https://ci.appveyor.com/project/apolukhin/stacktrace/branch/develop) | [](https://coveralls.io/r/apolukhin/stacktrace?branch=develop) | [details...](http://www.boost.org/development/tests/develop/developer/stacktrace.html)
|
||||
Master branch: | [](https://travis-ci.org/apolukhin/stacktrace) [](https://ci.appveyor.com/project/apolukhin/stacktrace/branch/master) | [](https://coveralls.io/r/apolukhin/stacktrace?branch=master) | [details...](http://www.boost.org/development/tests/master/developer/stacktrace.html)
|
||||
|
||||
|
||||
|
||||
@@ -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
16
build/has_addr2line.cpp
Normal 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());
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
;
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
199
include/boost/stacktrace/detail/addr2line_impls.hpp
Normal file
199
include/boost/stacktrace/detail/addr2line_impls.hpp
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
322
include/boost/stacktrace/detail/frame_msvc.ipp
Normal file
322
include/boost/stacktrace/detail/frame_msvc.ipp
Normal 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
|
||||
63
include/boost/stacktrace/detail/frame_noop.ipp
Normal file
63
include/boost/stacktrace/detail/frame_noop.ipp
Normal 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
|
||||
151
include/boost/stacktrace/detail/frame_unwind.ipp
Normal file
151
include/boost/stacktrace/detail/frame_unwind.ipp
Normal 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
|
||||
122
include/boost/stacktrace/detail/libbacktrace_impls.hpp
Normal file
122
include/boost/stacktrace/detail/libbacktrace_impls.hpp
Normal 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
|
||||
76
include/boost/stacktrace/detail/location_from_symbol.hpp
Normal file
76
include/boost/stacktrace/detail/location_from_symbol.hpp
Normal 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
|
||||
48
include/boost/stacktrace/detail/safe_dump_posix.ipp
Normal file
48
include/boost/stacktrace/detail/safe_dump_posix.ipp
Normal 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
|
||||
58
include/boost/stacktrace/detail/safe_dump_win.ipp
Normal file
58
include/boost/stacktrace/detail/safe_dump_win.ipp
Normal 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
|
||||
@@ -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
|
||||
|
||||
35
include/boost/stacktrace/detail/try_demangle.hpp
Normal file
35
include/boost/stacktrace/detail/try_demangle.hpp
Normal 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
|
||||
50
include/boost/stacktrace/detail/unwind_base_impls.hpp
Normal file
50
include/boost/stacktrace/detail/unwind_base_impls.hpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
166
include/boost/stacktrace/safe_dump_to.hpp
Normal file
166
include/boost/stacktrace/safe_dump_to.hpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
10
src/addr2line.cpp
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) ] ;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
102
test/test.cpp
102
test/test.cpp
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user