1// Copyright Antony Polukhin, 2016-2019. 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#ifndef BOOST_STACKTRACE_DETAIL_COLLECT_UNWIND_IPP 8#define BOOST_STACKTRACE_DETAIL_COLLECT_UNWIND_IPP 9 10#include <boost/config.hpp> 11#ifdef BOOST_HAS_PRAGMA_ONCE 12# pragma once 13#endif 14 15#include <boost/stacktrace/safe_dump_to.hpp> 16 17// On iOS 32-bit ARM architecture _Unwind_Backtrace function doesn't exist, symbol is undefined. 18// Forcing libc backtrace() function usage. 19#include <boost/predef.h> 20#if defined(BOOST_OS_IOS_AVAILABLE) && defined(BOOST_ARCH_ARM_AVAILABLE) && BOOST_VERSION_NUMBER_MAJOR(BOOST_ARCH_ARM) < 8 21#define BOOST_STACKTRACE_USE_LIBC_BACKTRACE_FUNCTION 22#endif 23 24#if defined(BOOST_STACKTRACE_USE_LIBC_BACKTRACE_FUNCTION) 25#include <execinfo.h> 26#include <algorithm> 27#else 28#include <unwind.h> 29#endif 30#include <cstdio> 31 32#if !defined(_GNU_SOURCE) && !defined(BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED) && !defined(BOOST_WINDOWS) 33#error "Boost.Stacktrace requires `_Unwind_Backtrace` function. Define `_GNU_SOURCE` macro or `BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED` if _Unwind_Backtrace is available without `_GNU_SOURCE`." 34#endif 35 36namespace boost { namespace stacktrace { namespace detail { 37 38#if !defined(BOOST_STACKTRACE_USE_LIBC_BACKTRACE_FUNCTION) 39struct unwind_state { 40 std::size_t frames_to_skip; 41 native_frame_ptr_t* current; 42 native_frame_ptr_t* end; 43}; 44 45inline _Unwind_Reason_Code unwind_callback(::_Unwind_Context* context, void* arg) { 46 // Note: do not write `::_Unwind_GetIP` because it is a macro on some platforms. 47 // Use `_Unwind_GetIP` instead! 48 unwind_state* const state = static_cast<unwind_state*>(arg); 49 if (state->frames_to_skip) { 50 --state->frames_to_skip; 51 return _Unwind_GetIP(context) ? ::_URC_NO_REASON : ::_URC_END_OF_STACK; 52 } 53 54 *state->current = reinterpret_cast<native_frame_ptr_t>( 55 _Unwind_GetIP(context) 56 ); 57 58 ++state->current; 59 if (!*(state->current - 1) || state->current == state->end) { 60 return ::_URC_END_OF_STACK; 61 } 62 return ::_URC_NO_REASON; 63} 64#endif //!defined(BOOST_STACKTRACE_USE_LIBC_BACKTRACE_FUNCTION) 65 66std::size_t this_thread_frames::collect(native_frame_ptr_t* out_frames, std::size_t max_frames_count, std::size_t skip) BOOST_NOEXCEPT { 67 std::size_t frames_count = 0; 68 if (!max_frames_count) { 69 return frames_count; 70 } 71 skip += 1; 72 73#if defined(BOOST_STACKTRACE_USE_LIBC_BACKTRACE_FUNCTION) 74 // According to https://opensource.apple.com/source/Libc/Libc-1272.200.26/gen/backtrace.c.auto.html 75 // it looks like the `::backtrace` is async signal safe. 76 frames_count = static_cast<size_t>(::backtrace(const_cast<void **>(out_frames), static_cast<int>(max_frames_count))); 77 78 // NOTE: There is no way to pass "skip" count to backtrace function so we need to perform left shift operation. 79 // If number of elements in result backtrace is >= max_frames_count then "skip" elements are wasted. 80 if (frames_count && skip) { 81 if (skip >= frames_count) { 82 frames_count = 0; 83 } else { 84 std::copy(out_frames + skip, out_frames + frames_count, out_frames); 85 frames_count -= skip; 86 } 87 } 88#else 89 boost::stacktrace::detail::unwind_state state = { skip, out_frames, out_frames + max_frames_count }; 90 ::_Unwind_Backtrace(&boost::stacktrace::detail::unwind_callback, &state); 91 frames_count = state.current - out_frames; 92#endif //defined(BOOST_STACKTRACE_USE_LIBC_BACKTRACE_FUNCTION) 93 94 if (frames_count && out_frames[frames_count - 1] == 0) { 95 -- frames_count; 96 } 97 98 return frames_count; 99} 100 101 102}}} // namespace boost::stacktrace::detail 103 104#undef BOOST_STACKTRACE_USE_LIBC_BACKTRACE_FUNCTION 105 106#endif // BOOST_STACKTRACE_DETAIL_COLLECT_UNWIND_IPP 107