| // Copyright Antony Polukhin, 2016-2019. |
| // |
| // Distributed under the Boost Software License, Version 1.0. (See |
| // accompanying file LICENSE_1_0.txt or copy at |
| // http://www.boost.org/LICENSE_1_0.txt) |
| |
| #ifndef BOOST_STACKTRACE_DETAIL_LIBBACKTRACE_IMPLS_HPP |
| #define BOOST_STACKTRACE_DETAIL_LIBBACKTRACE_IMPLS_HPP |
| |
| #include <boost/config.hpp> |
| #ifdef BOOST_HAS_PRAGMA_ONCE |
| # pragma once |
| #endif |
| |
| #include <boost/stacktrace/detail/to_hex_array.hpp> |
| #include <boost/stacktrace/detail/to_dec_array.hpp> |
| #include <boost/stacktrace/detail/location_from_symbol.hpp> |
| #include <boost/core/demangle.hpp> |
| |
| #ifdef BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE |
| # include BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE |
| #else |
| # include <backtrace.h> |
| #endif |
| |
| namespace boost { namespace stacktrace { namespace detail { |
| |
| |
| struct pc_data { |
| std::string* function; |
| std::string* filename; |
| std::size_t line; |
| }; |
| |
| inline void libbacktrace_syminfo_callback(void *data, uintptr_t /*pc*/, const char *symname, uintptr_t /*symval*/, uintptr_t /*symsize*/) { |
| pc_data& d = *static_cast<pc_data*>(data); |
| if (d.function && symname) { |
| *d.function = symname; |
| } |
| } |
| |
| // Old versions of libbacktrace have different signature for the callback |
| inline void libbacktrace_syminfo_callback(void *data, uintptr_t pc, const char *symname, uintptr_t symval) { |
| boost::stacktrace::detail::libbacktrace_syminfo_callback(data, pc, symname, symval, 0); |
| } |
| |
| inline int libbacktrace_full_callback(void *data, uintptr_t /*pc*/, const char *filename, int lineno, const char *function) { |
| pc_data& d = *static_cast<pc_data*>(data); |
| if (d.filename && filename) { |
| *d.filename = filename; |
| } |
| if (d.function && function) { |
| *d.function = function; |
| } |
| d.line = lineno; |
| return 0; |
| } |
| |
| inline void libbacktrace_error_callback(void* /*data*/, const char* /*msg*/, int /*errnum*/) BOOST_NOEXCEPT { |
| // Do nothing, just return. |
| } |
| |
| // Not async-signal-safe, so this method is not called from async-safe functions. |
| // |
| // This function is not async signal safe because: |
| // * Dynamic initialization of a block-scope variable with static storage duration could lock a mutex |
| // * No guarantees on `backtrace_create_state` function. |
| // |
| // Currently `backtrace_create_state` can not detect file name on Windows https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82543 |
| // That's why we provide a `prog_location` here. |
| BOOST_SYMBOL_VISIBLE inline ::backtrace_state* construct_state(const program_location& prog_location) BOOST_NOEXCEPT { |
| // [dcl.inline]: A static local variable in an inline function with external linkage always refers to the same object. |
| |
| // TODO: The most obvious solution: |
| // |
| //static ::backtrace_state* state = ::backtrace_create_state( |
| // prog_location.name(), |
| // 1, // allow safe concurrent usage of the same state |
| // boost::stacktrace::detail::libbacktrace_error_callback, |
| // 0 // pointer to data that will be passed to callback |
| //); |
| // |
| // |
| // Unfortunately, that solution segfaults when `construct_state()` function is in .so file |
| // and multiple threads concurrently work with state. |
| |
| |
| #ifndef BOOST_HAS_THREADS |
| static |
| #else |
| |
| // Result of `construct_state()` invocation is not stored by the callers, so `thread_local` |
| // gives a single `state` per thread and that state is not shared between threads in any way. |
| |
| # ifndef BOOST_NO_CXX11_THREAD_LOCAL |
| thread_local |
| # elif defined(__GNUC__) |
| static __thread |
| # else |
| /* just a local variable */ |
| # endif |
| |
| #endif |
| ::backtrace_state* state = ::backtrace_create_state( |
| prog_location.name(), |
| 0, |
| boost::stacktrace::detail::libbacktrace_error_callback, |
| 0 |
| ); |
| return state; |
| } |
| |
| struct to_string_using_backtrace { |
| std::string res; |
| boost::stacktrace::detail::program_location prog_location; |
| ::backtrace_state* state; |
| std::string filename; |
| std::size_t line; |
| |
| void prepare_function_name(const void* addr) { |
| boost::stacktrace::detail::pc_data data = {&res, &filename, 0}; |
| if (state) { |
| ::backtrace_pcinfo( |
| state, |
| reinterpret_cast<uintptr_t>(addr), |
| boost::stacktrace::detail::libbacktrace_full_callback, |
| boost::stacktrace::detail::libbacktrace_error_callback, |
| &data |
| ) |
| || |
| ::backtrace_syminfo( |
| state, |
| reinterpret_cast<uintptr_t>(addr), |
| boost::stacktrace::detail::libbacktrace_syminfo_callback, |
| boost::stacktrace::detail::libbacktrace_error_callback, |
| &data |
| ); |
| } |
| line = data.line; |
| } |
| |
| bool prepare_source_location(const void* /*addr*/) { |
| if (filename.empty() || !line) { |
| return false; |
| } |
| |
| res += " at "; |
| res += filename; |
| res += ':'; |
| res += boost::stacktrace::detail::to_dec_array(line).data(); |
| return true; |
| } |
| |
| to_string_using_backtrace() BOOST_NOEXCEPT { |
| state = boost::stacktrace::detail::construct_state(prog_location); |
| } |
| }; |
| |
| template <class Base> class to_string_impl_base; |
| typedef to_string_impl_base<to_string_using_backtrace> to_string_impl; |
| |
| inline std::string name_impl(const void* addr) { |
| std::string res; |
| |
| boost::stacktrace::detail::program_location prog_location; |
| ::backtrace_state* state = boost::stacktrace::detail::construct_state(prog_location); |
| |
| boost::stacktrace::detail::pc_data data = {&res, 0, 0}; |
| if (state) { |
| ::backtrace_pcinfo( |
| state, |
| reinterpret_cast<uintptr_t>(addr), |
| boost::stacktrace::detail::libbacktrace_full_callback, |
| boost::stacktrace::detail::libbacktrace_error_callback, |
| &data |
| ) |
| || |
| ::backtrace_syminfo( |
| state, |
| reinterpret_cast<uintptr_t>(addr), |
| boost::stacktrace::detail::libbacktrace_syminfo_callback, |
| boost::stacktrace::detail::libbacktrace_error_callback, |
| &data |
| ); |
| } |
| if (!res.empty()) { |
| res = boost::core::demangle(res.c_str()); |
| } |
| |
| return res; |
| } |
| |
| } // namespace detail |
| |
| std::string frame::source_file() const { |
| std::string res; |
| |
| boost::stacktrace::detail::program_location prog_location; |
| ::backtrace_state* state = boost::stacktrace::detail::construct_state(prog_location); |
| |
| boost::stacktrace::detail::pc_data data = {0, &res, 0}; |
| if (state) { |
| ::backtrace_pcinfo( |
| state, |
| reinterpret_cast<uintptr_t>(addr_), |
| boost::stacktrace::detail::libbacktrace_full_callback, |
| boost::stacktrace::detail::libbacktrace_error_callback, |
| &data |
| ); |
| } |
| |
| return res; |
| } |
| |
| std::size_t frame::source_line() const { |
| boost::stacktrace::detail::program_location prog_location; |
| ::backtrace_state* state = boost::stacktrace::detail::construct_state(prog_location); |
| |
| boost::stacktrace::detail::pc_data data = {0, 0, 0}; |
| if (state) { |
| ::backtrace_pcinfo( |
| state, |
| reinterpret_cast<uintptr_t>(addr_), |
| boost::stacktrace::detail::libbacktrace_full_callback, |
| boost::stacktrace::detail::libbacktrace_error_callback, |
| &data |
| ); |
| } |
| |
| return data.line; |
| } |
| |
| |
| }} // namespace boost::stacktrace |
| |
| #endif // BOOST_STACKTRACE_DETAIL_LIBBACKTRACE_IMPLS_HPP |