libbacktrace_impls.hpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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_LIBBACKTRACE_IMPLS_HPP
  7. #define BOOST_STACKTRACE_DETAIL_LIBBACKTRACE_IMPLS_HPP
  8. #include <boost/config.hpp>
  9. #ifdef BOOST_HAS_PRAGMA_ONCE
  10. # pragma once
  11. #endif
  12. #include <boost/stacktrace/detail/to_hex_array.hpp>
  13. #include <boost/stacktrace/detail/to_dec_array.hpp>
  14. #include <boost/stacktrace/detail/location_from_symbol.hpp>
  15. #include <boost/core/demangle.hpp>
  16. #ifdef BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE
  17. # include BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE
  18. #else
  19. # include <backtrace.h>
  20. #endif
  21. namespace boost { namespace stacktrace { namespace detail {
  22. struct pc_data {
  23. std::string* function;
  24. std::string* filename;
  25. std::size_t line;
  26. };
  27. inline void libbacktrace_syminfo_callback(void *data, uintptr_t /*pc*/, const char *symname, uintptr_t /*symval*/, uintptr_t /*symsize*/) {
  28. pc_data& d = *static_cast<pc_data*>(data);
  29. if (d.function && symname) {
  30. *d.function = symname;
  31. }
  32. }
  33. // Old versions of libbacktrace have different signature for the callback
  34. inline void libbacktrace_syminfo_callback(void *data, uintptr_t pc, const char *symname, uintptr_t symval) {
  35. boost::stacktrace::detail::libbacktrace_syminfo_callback(data, pc, symname, symval, 0);
  36. }
  37. inline int libbacktrace_full_callback(void *data, uintptr_t /*pc*/, const char *filename, int lineno, const char *function) {
  38. pc_data& d = *static_cast<pc_data*>(data);
  39. if (d.filename && filename) {
  40. *d.filename = filename;
  41. }
  42. if (d.function && function) {
  43. *d.function = function;
  44. }
  45. d.line = lineno;
  46. return 0;
  47. }
  48. inline void libbacktrace_error_callback(void* /*data*/, const char* /*msg*/, int /*errnum*/) noexcept {
  49. // Do nothing, just return.
  50. }
  51. // Not async-signal-safe, so this method is not called from async-safe functions.
  52. //
  53. // This function is not async signal safe because:
  54. // * Dynamic initialization of a block-scope variable with static storage duration could lock a mutex
  55. // * No guarantees on `backtrace_create_state` function.
  56. //
  57. // Currently `backtrace_create_state` can not detect file name on Windows https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82543
  58. // That's why we provide a `prog_location` here.
  59. BOOST_SYMBOL_VISIBLE inline ::backtrace_state* construct_state(const program_location& prog_location) noexcept {
  60. // [dcl.inline]: A static local variable in an inline function with external linkage always refers to the same object.
  61. // TODO: The most obvious solution:
  62. //
  63. //static ::backtrace_state* state = ::backtrace_create_state(
  64. // prog_location.name(),
  65. // 1, // allow safe concurrent usage of the same state
  66. // boost::stacktrace::detail::libbacktrace_error_callback,
  67. // 0 // pointer to data that will be passed to callback
  68. //);
  69. //
  70. //
  71. // Unfortunately, that solution segfaults when `construct_state()` function is in .so file
  72. // and multiple threads concurrently work with state. I failed to localize the root cause:
  73. // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=87653
  74. #define BOOST_STACKTRACE_DETAIL_IS_MT 1
  75. #if !defined(BOOST_HAS_THREADS)
  76. # define BOOST_STACKTRACE_DETAIL_STORAGE static
  77. # undef BOOST_STACKTRACE_DETAIL_IS_MT
  78. # define BOOST_STACKTRACE_DETAIL_IS_MT 0
  79. #elif defined(BOOST_STACKTRACE_BACKTRACE_FORCE_STATIC)
  80. # define BOOST_STACKTRACE_DETAIL_STORAGE static
  81. #elif !defined(BOOST_NO_CXX11_THREAD_LOCAL)
  82. # define BOOST_STACKTRACE_DETAIL_STORAGE thread_local
  83. #elif defined(__GNUC__) && !defined(__clang__)
  84. # define BOOST_STACKTRACE_DETAIL_STORAGE static __thread
  85. #else
  86. # define BOOST_STACKTRACE_DETAIL_STORAGE /* just a local variable */
  87. #endif
  88. BOOST_STACKTRACE_DETAIL_STORAGE ::backtrace_state* state = ::backtrace_create_state(
  89. prog_location.name(),
  90. BOOST_STACKTRACE_DETAIL_IS_MT,
  91. boost::stacktrace::detail::libbacktrace_error_callback,
  92. 0
  93. );
  94. #undef BOOST_STACKTRACE_DETAIL_IS_MT
  95. #undef BOOST_STACKTRACE_DETAIL_STORAGE
  96. return state;
  97. }
  98. struct to_string_using_backtrace {
  99. std::string res;
  100. boost::stacktrace::detail::program_location prog_location;
  101. ::backtrace_state* state;
  102. std::string filename;
  103. std::size_t line;
  104. void prepare_function_name(const void* addr) {
  105. boost::stacktrace::detail::pc_data data = {&res, &filename, 0};
  106. if (state) {
  107. ::backtrace_pcinfo(
  108. state,
  109. reinterpret_cast<uintptr_t>(addr),
  110. boost::stacktrace::detail::libbacktrace_full_callback,
  111. boost::stacktrace::detail::libbacktrace_error_callback,
  112. &data
  113. )
  114. ||
  115. ::backtrace_syminfo(
  116. state,
  117. reinterpret_cast<uintptr_t>(addr),
  118. boost::stacktrace::detail::libbacktrace_syminfo_callback,
  119. boost::stacktrace::detail::libbacktrace_error_callback,
  120. &data
  121. );
  122. }
  123. line = data.line;
  124. }
  125. bool prepare_source_location(const void* /*addr*/) {
  126. if (filename.empty() || !line) {
  127. return false;
  128. }
  129. res += " at ";
  130. res += filename;
  131. res += ':';
  132. res += boost::stacktrace::detail::to_dec_array(line).data();
  133. return true;
  134. }
  135. to_string_using_backtrace() noexcept {
  136. state = boost::stacktrace::detail::construct_state(prog_location);
  137. }
  138. };
  139. template <class Base> class to_string_impl_base;
  140. typedef to_string_impl_base<to_string_using_backtrace> to_string_impl;
  141. inline std::string name_impl(const void* addr) {
  142. std::string res;
  143. boost::stacktrace::detail::program_location prog_location;
  144. ::backtrace_state* state = boost::stacktrace::detail::construct_state(prog_location);
  145. boost::stacktrace::detail::pc_data data = {&res, 0, 0};
  146. if (state) {
  147. ::backtrace_pcinfo(
  148. state,
  149. reinterpret_cast<uintptr_t>(addr),
  150. boost::stacktrace::detail::libbacktrace_full_callback,
  151. boost::stacktrace::detail::libbacktrace_error_callback,
  152. &data
  153. )
  154. ||
  155. ::backtrace_syminfo(
  156. state,
  157. reinterpret_cast<uintptr_t>(addr),
  158. boost::stacktrace::detail::libbacktrace_syminfo_callback,
  159. boost::stacktrace::detail::libbacktrace_error_callback,
  160. &data
  161. );
  162. }
  163. if (!res.empty()) {
  164. res = boost::core::demangle(res.c_str());
  165. }
  166. return res;
  167. }
  168. } // namespace detail
  169. std::string frame::source_file() const {
  170. std::string res;
  171. if (!addr_) {
  172. return res;
  173. }
  174. boost::stacktrace::detail::program_location prog_location;
  175. ::backtrace_state* state = boost::stacktrace::detail::construct_state(prog_location);
  176. boost::stacktrace::detail::pc_data data = {0, &res, 0};
  177. if (state) {
  178. ::backtrace_pcinfo(
  179. state,
  180. reinterpret_cast<uintptr_t>(addr_),
  181. boost::stacktrace::detail::libbacktrace_full_callback,
  182. boost::stacktrace::detail::libbacktrace_error_callback,
  183. &data
  184. );
  185. }
  186. return res;
  187. }
  188. std::size_t frame::source_line() const {
  189. if (!addr_) {
  190. return 0;
  191. }
  192. boost::stacktrace::detail::program_location prog_location;
  193. ::backtrace_state* state = boost::stacktrace::detail::construct_state(prog_location);
  194. boost::stacktrace::detail::pc_data data = {0, 0, 0};
  195. if (state) {
  196. ::backtrace_pcinfo(
  197. state,
  198. reinterpret_cast<uintptr_t>(addr_),
  199. boost::stacktrace::detail::libbacktrace_full_callback,
  200. boost::stacktrace::detail::libbacktrace_error_callback,
  201. &data
  202. );
  203. }
  204. return data.line;
  205. }
  206. }} // namespace boost::stacktrace
  207. #endif // BOOST_STACKTRACE_DETAIL_LIBBACKTRACE_IMPLS_HPP