Skip to content

Commit fd51339

Browse files
committed
Async signal safe backend implmented and notes about async-safety were added to the docs.
1 parent 7cbe206 commit fd51339

14 files changed

Lines changed: 182 additions & 35 deletions

File tree

build/Jamfile.v2

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ project
1212
;
1313

1414
lib dl : : <link>shared ;
15+
lib gcc_s : : <link>shared ;
1516
lib Dbghelp ;
1617

1718
actions mp_simple_run_action
@@ -31,6 +32,9 @@ rule mp-run-simple ( sources + : args * : input-files * : requirements * : targe
3132
mp-run-simple has_backtrace.cpp : : : : backtrace ;
3233
explicit backtrace ;
3334

35+
mp-run-simple has_unwind.cpp : : : : unwind ;
36+
explicit unwind ;
37+
3438
mp-run-simple has_windbg.cpp : : : : WinDbg ;
3539
explicit WinDbg ;
3640

@@ -62,6 +66,21 @@ lib boost_stacktrace_backtrace
6266

6367
boost-install boost_stacktrace_backtrace ;
6468

69+
lib boost_stacktrace_unwind
70+
: # sources
71+
../src/unwind.cpp
72+
: # requirements
73+
<warnings>all
74+
<target-os>linux:<library>dl
75+
<link>shared:<define>BOOST_STACKTRACE_DYN_LINK=1
76+
[ check-target-builds ../build//unwind : : <build>no ]
77+
: # default build
78+
: # usage-requirements
79+
#<link>shared:<define>BOOST_STACKTRACE_DYN_LINK=1
80+
;
81+
82+
boost-install boost_stacktrace_unwind ;
83+
6584
lib boost_stacktrace_windbg
6685
: # sources
6786
../src/windbg.cpp

build/has_backtrace.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
// Copyright Antony Polukhin, 2016.
2+
//
3+
// Distributed under the Boost Software License, Version 1.0. (See
4+
// accompanying file LICENSE_1_0.txt or copy at
5+
// http://www.boost.org/LICENSE_1_0.txt)
16

27
#include <dlfcn.h>
38
#include <execinfo.h>

build/has_unwind.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright Antony Polukhin, 2016.
2+
//
3+
// Distributed under the Boost Software License, Version 1.0. (See
4+
// accompanying file LICENSE_1_0.txt or copy at
5+
// http://www.boost.org/LICENSE_1_0.txt)
6+
7+
#include <unwind.h>
8+
9+
int main() {
10+
11+
}

doc/stacktrace.qbk

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,11 +247,11 @@ By default Boost.Stacktrace is a header-only library and it attempts to detect t
247247
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*):
248248

249249
[table:configbackend Header only backend specifications
250-
[[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.]] [Construction speed] [Async signal safe[footnote Absolutely safe to use that backend in async signal handler.]]]
251-
[[*BOOST_STACKTRACE_USE_LIBUNWIND*] [Use libunwind tracing backend. This is the best known backend for POSIX systems that requires linking with libunwind library.] [POSIX] [yes] [yes] [slow] [no]]
252-
[[*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] [fast] [no]]
253-
[[*BOOST_STACKTRACE_USE_BACKTRACE*] [Use tracing backend that calls POSIX function backtrace. This is a fallback backend for POSIX platforms that requires linking with libdl library.] [POSIX] [no] [yes] [fast] [no]]
254-
[[*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] [noop] [yes]]
250+
[[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.]]]
251+
[[*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] [Constructors]]
252+
[[*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] [???]]
253+
[[*BOOST_STACKTRACE_USE_BACKTRACE*] [Use tracing backend that calls POSIX function `backtrace`. Requires linking with libdl library.] [POSIX] [no] [yes] [no]]
254+
[[*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] [all functions]]
255255
]
256256

257257

@@ -264,7 +264,7 @@ You may use the following macros to improve build times or to be able to switch
264264

265265
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:
266266

267-
* boost_stacktrace_libunwind
267+
* boost_stacktrace_unwind
268268
* boost_stacktrace_windbg
269269
* boost_stacktrace_backtrace
270270
* boost_stacktrace_noop

example/getting_started.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ void my_terminate_handler() {
9999
void my_signal_handler(int signum) {
100100
boost::stacktrace::stacktrace bt;
101101
if (bt) {
102-
std::cerr << "Signal " << signum << ", backtrace:\n" << boost::stacktrace::stacktrace() << '\n'; // ``[footnote Strictly speaking this code is not async-signal-safe, but we have SIGSEGV already it could hardly become worse. [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. ]``
102+
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. ]``
103103
}
104104
std::abort();
105105
}

include/boost/stacktrace/const_iterator.hpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,22 @@
2020

2121
namespace boost { namespace stacktrace {
2222

23+
24+
#ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED
25+
/// Random access iterator over frames that returns boost::stacktrace::frame on dereference
26+
class const_iterator: implementation_details {};
27+
28+
#else
29+
2330
// Forward declarations
2431
class stacktrace;
2532

26-
/// Random access iterator over frames that returns `frame` on dereference.
2733
class const_iterator: public boost::iterator_facade<
2834
const_iterator,
2935
frame,
3036
boost::random_access_traversal_tag,
3137
frame>
3238
{
33-
/// @cond
3439
typedef boost::iterator_facade<
3540
const_iterator,
3641
frame,
@@ -73,9 +78,10 @@ class const_iterator: public boost::iterator_facade<
7378
BOOST_ASSERT(impl_ == it.impl_);
7479
return it.frame_no_ - frame_no_;
7580
}
76-
/// @endcond
7781
};
7882

83+
#endif // #ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED
84+
7985
}} // namespace boost::stacktrace
8086

8187
#endif // BOOST_STACKTRACE_CONST_ITERATOR_HPP

include/boost/stacktrace/detail/backend.hpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,21 @@
2525
#endif
2626

2727
// Backend autodetection
28-
#if !defined(BOOST_STACKTRACE_USE_NOOP) && !defined(BOOST_STACKTRACE_USE_WINDBG) && !defined(BOOST_STACKTRACE_USE_LIBUNWIND) \
29-
&& !defined(BOOST_STACKTRACE_USE_BACKTRACE) &&!defined(BOOST_STACKTRACE_USE_HEADER)
28+
#if !defined(BOOST_STACKTRACE_USE_NOOP) && !defined(BOOST_STACKTRACE_USE_WINDBG) && !defined(BOOST_STACKTRACE_USE_UNWIND) \
29+
&& !defined(BOOST_STACKTRACE_USE_BACKTRACE) && !defined(BOOST_STACKTRACE_USE_HEADER)
3030

3131
#if defined(__has_include) && (!defined(__GNUC__) || __GNUC__ > 4 || BOOST_CLANG)
32-
# if __has_include(<execinfo.h>)
33-
# define BOOST_STACKTRACE_USE_BACKTRACE
34-
# elif __has_include("Dbgeng.h")
32+
# if __has_include("Dbgeng.h")
3533
# define BOOST_STACKTRACE_USE_WINDBG
34+
# else
35+
# define BOOST_STACKTRACE_USE_UNWIND
3636
# endif
3737
#else
3838
# if defined(BOOST_WINDOWS)
3939
# define BOOST_STACKTRACE_USE_WINDBG
4040
# else
41-
# define BOOST_STACKTRACE_USE_BACKTRACE
41+
# define BOOST_STACKTRACE_USE_UNWIND
42+
//# define BOOST_STACKTRACE_USE_BACKTRACE
4243
# endif
4344
#endif
4445

include/boost/stacktrace/detail/backend.ipp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# include <boost/stacktrace/detail/backend_noop.hpp>
1919
#elif defined(BOOST_STACKTRACE_USE_WINDBG)
2020
# include <boost/stacktrace/detail/backend_windows.hpp>
21-
#elif defined(BOOST_STACKTRACE_USE_BACKTRACE)
21+
#elif defined(BOOST_STACKTRACE_USE_BACKTRACE) || defined(BOOST_STACKTRACE_USE_UNWIND)
2222
# include <boost/stacktrace/detail/backend_linux.hpp>
2323
#else
2424
# error No suitable backtrace backend found

include/boost/stacktrace/detail/backend_linux.hpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
#include <boost/lexical_cast/try_lexical_convert.hpp>
1919
#include <algorithm>
2020

21+
#if defined(BOOST_STACKTRACE_USE_UNWIND)
22+
#include <unwind.h>
23+
#endif
24+
2125
#include <dlfcn.h>
2226
#include <execinfo.h>
2327
#include <cstdio>
@@ -163,18 +167,51 @@ static inline std::string try_demangle(const char* mangled) {
163167
return res;
164168
}
165169

170+
171+
172+
173+
#if defined(BOOST_STACKTRACE_USE_UNWIND)
174+
struct unwind_state {
175+
void** current;
176+
void** end;
177+
};
178+
179+
inline _Unwind_Reason_Code unwind_callback(struct _Unwind_Context* context, void* arg) {
180+
unwind_state* state = static_cast<unwind_state*>(arg);
181+
*state->current = reinterpret_cast<void*>(
182+
_Unwind_GetIP(context)
183+
);
184+
185+
++state->current;
186+
if (!*(state->current - 1) || state->current == state->end) {
187+
return _URC_END_OF_STACK;
188+
}
189+
return _URC_NO_REASON;
190+
}
191+
#endif
192+
193+
194+
195+
166196
backend::backend(void* memory, std::size_t size, std::size_t& hash_code) BOOST_NOEXCEPT
167197
: data_(static_cast<backtrace_holder*>(memory))
168198
{
169199
new (data_) backtrace_holder();
170200
data_->frames_count = 0;
171201
hash_code = 0;
172202

173-
// TODO: Not async signal safe. Use _Unwind_Backtrace, _Unwind_GetIP
203+
#if defined(BOOST_STACKTRACE_USE_UNWIND)
204+
unwind_state state = { data_->buffer, data_->buffer + data_->frames_count };
205+
_Unwind_Backtrace(&unwind_callback, &state);
206+
data_->frames_count = state.current - data_->buffer;
207+
#elif defined(BOOST_STACKTRACE_USE_BACKTRACE)
174208
data_->frames_count = ::backtrace(data_->buffer, (size - sizeof(backtrace_holder)) / sizeof(void*));
175209
if (data_->buffer[data_->frames_count - 1] == 0) {
176210
-- data_->frames_count;
177211
}
212+
#else
213+
# error No stacktrace backend defined. Define BOOST_STACKTRACE_USE_UNWIND or BOOST_STACKTRACE_USE_BACKTRACE
214+
#endif
178215

179216
hash_code = boost::hash_range(data_->buffer, data_->buffer + data_->frames_count);
180217
}

include/boost/stacktrace/frame.hpp

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,33 +35,43 @@ class frame {
3535
frame() = delete;
3636

3737
/// @brief Copy constructs frame.
38-
/// @throws Nothing.
3938
///
4039
/// @b Complexity: O(1).
40+
///
41+
/// @b Async-Handler-Safety: Safe.
42+
/// @throws Nothing.
4143
frame(const frame&) = default;
4244

4345
/// @brief Copy assigns frame.
44-
/// @throws Nothing.
4546
///
4647
/// @b Complexity: O(1).
48+
///
49+
/// @b Async-Handler-Safety: Safe.
50+
/// @throws Nothing.
4751
frame& operator=(const frame&) = default;
4852
#endif
4953

5054
/// @brief Constructs frame that can extract information from addr at runtime.
51-
/// @throws Nothing.
5255
///
5356
/// @b Complexity: O(1).
57+
///
58+
/// @b Async-Handler-Safety: Safe.
59+
/// @throws Nothing.
5460
explicit frame(const void* addr) BOOST_NOEXCEPT
5561
: addr_(addr)
5662
{}
5763

5864
/// @returns Name of the frame (function name in a human readable form).
65+
///
66+
/// @b Async-Handler-Safety: Unsafe.
5967
/// @throws std::bad_alloc if not enough memory to construct resulting string.
6068
std::string name() const {
6169
return boost::stacktrace::detail::backend::get_name(address());
6270
}
6371

6472
/// @returns Address of the frame function.
73+
///
74+
/// @b Async-Handler-Safety: Safe.
6575
/// @throws Nothing.
6676
const void* address() const BOOST_NOEXCEPT {
6777
return addr_;
@@ -70,31 +80,35 @@ class frame {
7080
/// @returns Path to the source file, were the function of the frame is defined. Returns empty string
7181
/// if this->source_line() == 0.
7282
/// @throws std::bad_alloc if not enough memory to construct resulting string.
83+
///
84+
/// @b Async-Handler-Safety: Unsafe.
7385
std::string source_file() const {
7486
return boost::stacktrace::detail::backend::get_source_file(address());
7587
}
7688

7789
/// @returns Code line in the source file, were the function of the frame is defined.
7890
/// @throws std::bad_alloc if not enough memory to construct string for internal needs.
91+
///
92+
/// @b Async-Handler-Safety: Unsafe.
7993
std::size_t source_line() const {
8094
return boost::stacktrace::detail::backend::get_source_line(address());
8195
}
8296
};
8397

84-
/// Comparison operators that provide platform dependant ordering and have O(1) complexity.
98+
/// Comparison operators that provide platform dependant ordering and have O(1) complexity; are Async-Handler-Safe.
8599
inline bool operator< (const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return lhs.address() < rhs.address(); }
86100
inline bool operator> (const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return rhs < lhs; }
87101
inline bool operator<=(const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return !(lhs > rhs); }
88102
inline bool operator>=(const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return !(lhs < rhs); }
89103
inline bool operator==(const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return lhs.address() == rhs.address(); }
90104
inline bool operator!=(const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return !(lhs == rhs); }
91105

92-
/// Hashing support, O(1) complexity.
106+
/// Hashing support, O(1) complexity; Async-Handler-Safe.
93107
inline std::size_t hash_value(const frame& f) BOOST_NOEXCEPT {
94108
return reinterpret_cast<std::size_t>(f.address());
95109
}
96110

97-
/// Outputs stacktrace::frame in a human readable format to output stream.
111+
/// Outputs stacktrace::frame in a human readable format to output stream; unsafe to use in async handlers.
98112
template <class CharT, class TraitsT>
99113
std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const frame& f) {
100114
os << f.name();

0 commit comments

Comments
 (0)