libbacktrace_impls.hpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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. #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*/) BOOST_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) BOOST_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.
  73. #ifndef BOOST_HAS_THREADS
  74. static
  75. #else
  76. // Result of `construct_state()` invocation is not stored by the callers, so `thread_local`
  77. // gives a single `state` per thread and that state is not shared between threads in any way.
  78. # ifndef BOOST_NO_CXX11_THREAD_LOCAL
  79. thread_local
  80. # elif defined(__GNUC__)
  81. static __thread
  82. # else
  83. /* just a local variable */
  84. # endif
  85. #endif
  86. ::backtrace_state* state = ::backtrace_create_state(
  87. prog_location.name(),
  88. 0,
  89. boost::stacktrace::detail::libbacktrace_error_callback,
  90. 0
  91. );
  92. return state;
  93. }
  94. struct to_string_using_backtrace {
  95. std::string res;
  96. boost::stacktrace::detail::program_location prog_location;
  97. ::backtrace_state* state;
  98. std::string filename;
  99. std::size_t line;
  100. void prepare_function_name(const void* addr) {
  101. boost::stacktrace::detail::pc_data data = {&res, &filename, 0};
  102. if (state) {
  103. ::backtrace_pcinfo(
  104. state,
  105. reinterpret_cast<uintptr_t>(addr),
  106. boost::stacktrace::detail::libbacktrace_full_callback,
  107. boost::stacktrace::detail::libbacktrace_error_callback,
  108. &data
  109. )
  110. ||
  111. ::backtrace_syminfo(
  112. state,
  113. reinterpret_cast<uintptr_t>(addr),
  114. boost::stacktrace::detail::libbacktrace_syminfo_callback,
  115. boost::stacktrace::detail::libbacktrace_error_callback,
  116. &data
  117. );
  118. }
  119. line = data.line;
  120. }
  121. bool prepare_source_location(const void* /*addr*/) {
  122. if (filename.empty() || !line) {
  123. return false;
  124. }
  125. res += " at ";
  126. res += filename;
  127. res += ':';
  128. res += boost::stacktrace::detail::to_dec_array(line).data();
  129. return true;
  130. }
  131. to_string_using_backtrace() BOOST_NOEXCEPT {
  132. state = boost::stacktrace::detail::construct_state(prog_location);
  133. }
  134. };
  135. template <class Base> class to_string_impl_base;
  136. typedef to_string_impl_base<to_string_using_backtrace> to_string_impl;
  137. inline std::string name_impl(const void* addr) {
  138. std::string res;
  139. boost::stacktrace::detail::program_location prog_location;
  140. ::backtrace_state* state = boost::stacktrace::detail::construct_state(prog_location);
  141. boost::stacktrace::detail::pc_data data = {&res, 0, 0};
  142. if (state) {
  143. ::backtrace_pcinfo(
  144. state,
  145. reinterpret_cast<uintptr_t>(addr),
  146. boost::stacktrace::detail::libbacktrace_full_callback,
  147. boost::stacktrace::detail::libbacktrace_error_callback,
  148. &data
  149. )
  150. ||
  151. ::backtrace_syminfo(
  152. state,
  153. reinterpret_cast<uintptr_t>(addr),
  154. boost::stacktrace::detail::libbacktrace_syminfo_callback,
  155. boost::stacktrace::detail::libbacktrace_error_callback,
  156. &data
  157. );
  158. }
  159. if (!res.empty()) {
  160. res = boost::core::demangle(res.c_str());
  161. }
  162. return res;
  163. }
  164. } // namespace detail
  165. std::string frame::source_file() const {
  166. std::string res;
  167. if (!addr_) {
  168. return res;
  169. }
  170. boost::stacktrace::detail::program_location prog_location;
  171. ::backtrace_state* state = boost::stacktrace::detail::construct_state(prog_location);
  172. boost::stacktrace::detail::pc_data data = {0, &res, 0};
  173. if (state) {
  174. ::backtrace_pcinfo(
  175. state,
  176. reinterpret_cast<uintptr_t>(addr_),
  177. boost::stacktrace::detail::libbacktrace_full_callback,
  178. boost::stacktrace::detail::libbacktrace_error_callback,
  179. &data
  180. );
  181. }
  182. return res;
  183. }
  184. std::size_t frame::source_line() const {
  185. if (!addr_) {
  186. return 0;
  187. }
  188. boost::stacktrace::detail::program_location prog_location;
  189. ::backtrace_state* state = boost::stacktrace::detail::construct_state(prog_location);
  190. boost::stacktrace::detail::pc_data data = {0, 0, 0};
  191. if (state) {
  192. ::backtrace_pcinfo(
  193. state,
  194. reinterpret_cast<uintptr_t>(addr_),
  195. boost::stacktrace::detail::libbacktrace_full_callback,
  196. boost::stacktrace::detail::libbacktrace_error_callback,
  197. &data
  198. );
  199. }
  200. return data.line;
  201. }
  202. }} // namespace boost::stacktrace
  203. #endif // BOOST_STACKTRACE_DETAIL_LIBBACKTRACE_IMPLS_HPP