collect_unwind.ipp 3.8 KB

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