diff --git a/include/boost/stacktrace/detail/addr2line_impls.hpp b/include/boost/stacktrace/detail/addr2line_impls.hpp new file mode 100644 index 0000000..67657c6 --- /dev/null +++ b/include/boost/stacktrace/detail/addr2line_impls.hpp @@ -0,0 +1,201 @@ +// Copyright Antony Polukhin, 2016-2017. +// +// 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_ADDR2LINE_IMPLS_HPP +#define BOOST_STACKTRACE_DETAIL_ADDR2LINE_IMPLS_HPP + +#include +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace boost { namespace stacktrace { namespace detail { + +class addr2line_pipe { + ::FILE* p; + ::pid_t pid; + +public: + explicit addr2line_pipe(const char *flag, const char* exec_path, const char* addr) BOOST_NOEXCEPT + : p(0) + , pid(0) + { + int pdes[2]; + char prog_name[] = "addr2line"; + char* argp[] = { + prog_name, + const_cast(flag), + const_cast(exec_path), + const_cast(addr), + 0 + }; + + if (::pipe(pdes) < 0) { + return; + } + + pid = ::fork(); + switch (pid) { + case -1: + // failed + ::close(pdes[0]); + ::close(pdes[1]); + return; + + case 0: + // we are the child + ::close(STDERR_FILENO); + ::close(pdes[0]); + if (pdes[1] != STDOUT_FILENO) { + ::dup2(pdes[1], STDOUT_FILENO); + } + ::execvp(prog_name, argp); + ::_exit(127); + } + + p = ::fdopen(pdes[0], "r"); + ::close(pdes[1]); + } + + operator ::FILE*() const BOOST_NOEXCEPT { + return p; + } + + ~addr2line_pipe() BOOST_NOEXCEPT { + if (p) { + ::fclose(p); + int pstat = 0; + ::kill(pid, SIGKILL); + ::waitpid(pid, &pstat, 0); + } + } +}; + +inline std::string addr2line(const char* flag, const void* addr) { + std::string res; + + ::Dl_info dli; + if (!!::dladdr(addr, &dli) && dli.dli_fname) { + res = dli.dli_fname; + } else { + res.resize(16); + int rlin_size = ::readlink("/proc/self/exe", &res[0], res.size() - 1); + while (rlin_size == static_cast(res.size() - 1)) { + res.resize(res.size() * 4); + rlin_size = ::readlink("/proc/self/exe", &res[0], res.size() - 1); + } + if (rlin_size == -1) { + res.clear(); + return res; + } + res.resize(rlin_size); + } + + addr2line_pipe p(flag, res.c_str(), to_hex_array(addr).data()); + res.clear(); + + if (!p) { + return res; + } + + char data[32]; + while (!::feof(p)) { + if (::fgets(data, sizeof(data), p)) { + res += data; + } else { + break; + } + } + + // Trimming + while (!res.empty() && (res[res.size() - 1] == '\n' || res[res.size() - 1] == '\r')) { + res.erase(res.size() - 1); + } + + return res; +} + + +struct to_string_using_addr2line { + std::string res; + void prepare_function_name(const void* addr) { + res = boost::stacktrace::frame(addr).name(); + } + + bool prepare_source_location(const void* addr) { + //return addr2line("-Cfipe", addr); // Does not seem to work in all cases + std::string source_line = boost::stacktrace::detail::addr2line("-Cpe", addr); + if (!source_line.empty() && source_line[0] != '?') { + res += " at "; + res += source_line; + return true; + } + + return false; + } +}; + +template class to_string_impl_base; +typedef to_string_impl_base to_string_impl; + +inline std::string name_impl(const void* addr) { + std::string res = boost::stacktrace::detail::addr2line("-fe", addr); + res = res.substr(0, res.find_last_of('\n')); + res = boost::stacktrace::detail::try_demangle(res.c_str()); + + if (res == "??") { + res.clear(); + } + + return res; +} + +} // namespace detail + +std::string frame::source_file() const { + std::string res; + res = boost::stacktrace::detail::addr2line("-e", addr_); + res = res.substr(0, res.find_last_of(':')); + if (res == "??") { + res.clear(); + } + + return res; +} + + +std::size_t frame::source_line() const { + std::size_t line_num = 0; + std::string res = boost::stacktrace::detail::addr2line("-e", addr_); + const std::size_t last = res.find_last_of(':'); + if (last == std::string::npos) { + return 0; + } + res = res.substr(last + 1); + + if (!boost::conversion::try_lexical_convert(res, line_num)) { + return 0; + } + + return line_num; +} + + +}} // namespace boost::stacktrace + +#endif // BOOST_STACKTRACE_DETAIL_ADDR2LINE_IMPLS_HPP diff --git a/include/boost/stacktrace/detail/backend_posix.hpp b/include/boost/stacktrace/detail/backend_posix.hpp index 2d1a3d1..47a395f 100644 --- a/include/boost/stacktrace/detail/backend_posix.hpp +++ b/include/boost/stacktrace/detail/backend_posix.hpp @@ -12,170 +12,26 @@ # pragma once #endif -#include #include +#include #include #include -#ifdef BOOST_STACKTRACE_USE_BACKTRACE -#include -#endif - #include #include #include -#ifdef BOOST_STACKTRACE_USE_ADDR2LINE -#include -#include -#include +#ifdef BOOST_STACKTRACE_USE_BACKTRACE +# include +#elif defined(BOOST_STACKTRACE_USE_ADDR2LINE) +# include +#else +# include #endif namespace boost { namespace stacktrace { namespace detail { -#ifdef BOOST_STACKTRACE_USE_ADDR2LINE -class addr2line_pipe { - ::FILE* p; - ::pid_t pid; - -public: - explicit addr2line_pipe(const char *flag, const char* exec_path, const char* addr) BOOST_NOEXCEPT - : p(0) - , pid(0) - { - int pdes[2]; - char prog_name[] = "addr2line"; - char* argp[] = { - prog_name, - const_cast(flag), - const_cast(exec_path), - const_cast(addr), - 0 - }; - - if (::pipe(pdes) < 0) { - return; - } - - pid = ::fork(); - switch (pid) { - case -1: - // failed - ::close(pdes[0]); - ::close(pdes[1]); - return; - - case 0: - // we are the child - ::close(STDERR_FILENO); - ::close(pdes[0]); - if (pdes[1] != STDOUT_FILENO) { - ::dup2(pdes[1], STDOUT_FILENO); - } - ::execvp(prog_name, argp); - ::_exit(127); - } - - p = ::fdopen(pdes[0], "r"); - ::close(pdes[1]); - } - - operator ::FILE*() const BOOST_NOEXCEPT { - return p; - } - - ~addr2line_pipe() BOOST_NOEXCEPT { - if (p) { - ::fclose(p); - int pstat = 0; - ::kill(pid, SIGKILL); - ::waitpid(pid, &pstat, 0); - } - } -}; - -inline std::string addr2line(const char* flag, const void* addr) { - std::string res; - - ::Dl_info dli; - if (!!::dladdr(addr, &dli) && dli.dli_fname) { - res = dli.dli_fname; - } else { - res.resize(16); - int rlin_size = ::readlink("/proc/self/exe", &res[0], res.size() - 1); - while (rlin_size == static_cast(res.size() - 1)) { - res.resize(res.size() * 4); - rlin_size = ::readlink("/proc/self/exe", &res[0], res.size() - 1); - } - if (rlin_size == -1) { - res.clear(); - return res; - } - res.resize(rlin_size); - } - - addr2line_pipe p(flag, res.c_str(), to_hex_array(addr).data()); - res.clear(); - - if (!p) { - return res; - } - - char data[32]; - while (!::feof(p)) { - if (::fgets(data, sizeof(data), p)) { - res += data; - } else { - break; - } - } - - // Trimming - while (!res.empty() && (res[res.size() - 1] == '\n' || res[res.size() - 1] == '\r')) { - res.erase(res.size() - 1); - } - - return res; -} - -#endif // #ifdef BOOST_STACKTRACE_USE_ADDR2LINE - -#ifdef BOOST_STACKTRACE_USE_BACKTRACE -struct pc_data { - std::string* function; - std::string* filename; - std::size_t line; -}; - -static int libbacktrace_full_callback(void *data, uintptr_t /*pc*/, const char *filename, int lineno, const char *function) { - pc_data& d = *static_cast(data); - if (d.filename && filename) { - *d.filename = filename; - } - if (d.function && function) { - *d.function = function; - } - d.line = lineno; - return 0; -} -#endif - - - -inline std::string try_demangle(const char* mangled) { - std::string res; - - boost::core::scoped_demangled_name demangled(mangled); - if (demangled.get()) { - res = demangled.get(); - } else { - res = mangled; - } - - return res; -} - struct unwind_state { void** current; void** end; @@ -211,78 +67,40 @@ std::size_t backend::collect(void** memory, std::size_t size) BOOST_NOEXCEPT { return frames_count; } -#ifdef BOOST_STACKTRACE_USE_BACKTRACE -inline std::string to_string_impl(const void* addr, ::backtrace_state* state) { - std::string res; - std::string filename; +template +class to_string_impl_base: private Base { +public: + std::string operator()(const void* addr) { + Base::res.clear(); + Base::prepare_function_name(addr); + if (Base::res.empty()) { + Base::res = to_hex_array(addr).data(); + } - boost::stacktrace::detail::pc_data data = {&res, &filename, 0}; - ::backtrace_pcinfo(state, reinterpret_cast(addr), boost::stacktrace::detail::libbacktrace_full_callback, 0, &data); - if (res.empty()) { - res = to_hex_array(addr).data(); + if (Base::prepare_source_location(addr)) { + return Base::res; + } + + ::Dl_info dli; + if (!!::dladdr(addr, &dli) && dli.dli_sname) { + Base::res += " in "; + Base::res += dli.dli_fname; + } + + return Base::res; } - - if (!filename.empty() && data.line) { - res += " at "; - res += filename; - res += ':'; - res += boost::lexical_cast >(data.line).data(); - return res; - } - - ::Dl_info dli; - if (!!::dladdr(addr, &dli) && dli.dli_sname) { - res += " in "; - res += dli.dli_fname; - } - - return res; -} +}; std::string backend::to_string(const void* addr) { - ::backtrace_state* state = ::backtrace_create_state( - 0, 0, 0, 0 - ); - return boost::stacktrace::detail::to_string_impl(addr, state); + to_string_impl impl; + return impl(addr); } -#else - -std::string backend::to_string(const void* addr) { - std::string res = boost::stacktrace::frame(addr).name(); - if (res.empty()) { - res = to_hex_array(addr).data(); - } - -#ifdef BOOST_STACKTRACE_USE_ADDR2LINE - //return addr2line("-Cfipe", addr); // Does not seem to work in all cases - std::string source_line = boost::stacktrace::detail::addr2line("-Cpe", addr); - if (!source_line.empty() && source_line[0] != '?') { - res += " at "; - res += source_line; - return res; - } -#endif - - ::Dl_info dli; - if (!!::dladdr(addr, &dli) && dli.dli_sname) { - res += " in "; - res += dli.dli_fname; - } - - return res; -} -#endif - std::string backend::to_string(const frame* frames, std::size_t size) { std::string res; res.reserve(64 * size); -#ifdef BOOST_STACKTRACE_USE_BACKTRACE - ::backtrace_state* state = ::backtrace_create_state( - 0, 0, 0, 0 - ); -#endif + to_string_impl impl; for (std::size_t i = 0; i < size; ++i) { if (i < 10) { @@ -291,11 +109,7 @@ std::string backend::to_string(const frame* frames, std::size_t size) { res += boost::lexical_cast >(i).data(); res += '#'; res += ' '; -#ifdef BOOST_STACKTRACE_USE_BACKTRACE - res += boost::stacktrace::detail::to_string_impl(frames[i].address(), state); -#else - res += boost::stacktrace::detail::backend::to_string(frames[i].address()); -#endif + res += impl(frames[i].address()); res += '\n'; } @@ -303,97 +117,18 @@ std::string backend::to_string(const frame* frames, std::size_t size) { } - - } // namespace detail std::string frame::name() const { - std::string res; - ::Dl_info dli; const bool dl_ok = !!::dladdr(addr_, &dli); if (dl_ok && dli.dli_sname) { return boost::stacktrace::detail::try_demangle(dli.dli_sname); } -#ifdef BOOST_STACKTRACE_USE_BACKTRACE - ::backtrace_state* state = ::backtrace_create_state( - 0, 0, 0, 0 - ); - - boost::stacktrace::detail::pc_data data = {&res, 0, 0}; - ::backtrace_pcinfo(state, reinterpret_cast(addr_), boost::stacktrace::detail::libbacktrace_full_callback, 0, &data); - if (!res.empty()) { - res = boost::stacktrace::detail::try_demangle(res.c_str()); - return res; - } -#elif defined(BOOST_STACKTRACE_USE_ADDR2LINE) - res = boost::stacktrace::detail::addr2line("-fe", addr_); - res = res.substr(0, res.find_last_of('\n')); - res = boost::stacktrace::detail::try_demangle(res.c_str()); - - if (res == "??") { - res.clear(); - } -#endif - - return res; + return boost::stacktrace::detail::name_impl(addr_); } -std::string frame::source_file() const { - std::string res; - -#ifdef BOOST_STACKTRACE_USE_BACKTRACE - ::backtrace_state* state = ::backtrace_create_state( - 0, 0, 0, 0 - ); - - boost::stacktrace::detail::pc_data data = {0, &res, 0}; - ::backtrace_pcinfo(state, reinterpret_cast(addr_), boost::stacktrace::detail::libbacktrace_full_callback, 0, &data); - if (!res.empty()) { - return res; - } -#elif defined(BOOST_STACKTRACE_USE_ADDR2LINE) - res = boost::stacktrace::detail::addr2line("-e", addr_); - res = res.substr(0, res.find_last_of(':')); - if (res == "??") { - res.clear(); - } -#endif - - return res; -} - -std::size_t frame::source_line() const { - std::size_t line_num = 0; - -#ifdef BOOST_STACKTRACE_USE_BACKTRACE - ::backtrace_state* state = ::backtrace_create_state( - 0, 0, 0, 0 - ); - - boost::stacktrace::detail::pc_data data = {0, 0, 0}; - ::backtrace_pcinfo(state, reinterpret_cast(addr_), boost::stacktrace::detail::libbacktrace_full_callback, 0, &data); - if (data.line) { - return data.line; - } -#elif defined(BOOST_STACKTRACE_USE_ADDR2LINE) - std::string res = boost::stacktrace::detail::addr2line("-e", addr_); - const std::size_t last = res.find_last_of(':'); - if (last == std::string::npos) { - return 0; - } - res = res.substr(last + 1); - - if (!boost::conversion::try_lexical_convert(res, line_num)) { - return 0; - } -#endif - - return line_num; -} - - }} // namespace boost::stacktrace #endif // BOOST_STACKTRACE_DETAIL_BACKEND_POSIX_HPP diff --git a/include/boost/stacktrace/detail/libbacktrace_impls.hpp b/include/boost/stacktrace/detail/libbacktrace_impls.hpp new file mode 100644 index 0000000..29f1588 --- /dev/null +++ b/include/boost/stacktrace/detail/libbacktrace_impls.hpp @@ -0,0 +1,122 @@ +// Copyright Antony Polukhin, 2016. +// +// 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 +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include +#include +#include + +#include + +namespace boost { namespace stacktrace { namespace detail { + + +struct pc_data { + std::string* function; + std::string* filename; + std::size_t line; +}; + +inline int libbacktrace_full_callback(void *data, uintptr_t /*pc*/, const char *filename, int lineno, const char *function) { + pc_data& d = *static_cast(data); + if (d.filename && filename) { + *d.filename = filename; + } + if (d.function && function) { + *d.function = function; + } + d.line = lineno; + return 0; +} + +struct to_string_using_backtrace { + std::string res; + ::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}; + ::backtrace_pcinfo(state, reinterpret_cast(addr), boost::stacktrace::detail::libbacktrace_full_callback, 0, &data); + line = data.line; + } + + bool prepare_source_location(const void* /*addr*/) { + if (filename.empty() || !line) { + return false; + } + + res += " at "; + res += filename; + res += ':'; + res += boost::lexical_cast >(line).data(); + return true; + } + + to_string_using_backtrace() BOOST_NOEXCEPT { + state = ::backtrace_create_state( + 0, 0, 0, 0 + ); + } +}; + +template class to_string_impl_base; +typedef to_string_impl_base to_string_impl; + +inline std::string name_impl(const void* addr) { + std::string res; + + ::backtrace_state* state = ::backtrace_create_state( + 0, 0, 0, 0 + ); + + boost::stacktrace::detail::pc_data data = {&res, 0, 0}; + ::backtrace_pcinfo(state, reinterpret_cast(addr), boost::stacktrace::detail::libbacktrace_full_callback, 0, &data); + if (!res.empty()) { + res = boost::stacktrace::detail::try_demangle(res.c_str()); + } + + return res; +} + +} // namespace detail + +std::string frame::source_file() const { + std::string res; + + ::backtrace_state* state = ::backtrace_create_state( + 0, 0, 0, 0 + ); + + boost::stacktrace::detail::pc_data data = {0, &res, 0}; + ::backtrace_pcinfo(state, reinterpret_cast(addr_), boost::stacktrace::detail::libbacktrace_full_callback, 0, &data); + + return res; +} + +std::size_t frame::source_line() const { + ::backtrace_state* state = ::backtrace_create_state( + 0, 0, 0, 0 + ); + + boost::stacktrace::detail::pc_data data = {0, 0, 0}; + ::backtrace_pcinfo(state, reinterpret_cast(addr_), boost::stacktrace::detail::libbacktrace_full_callback, 0, &data); + + + return data.line; +} + + +}} // namespace boost::stacktrace + +#endif // BOOST_STACKTRACE_DETAIL_LIBBACKTRACE_IMPLS_HPP diff --git a/include/boost/stacktrace/detail/to_hex_array.hpp b/include/boost/stacktrace/detail/to_hex_array.hpp index 57f8233..dd55ded 100644 --- a/include/boost/stacktrace/detail/to_hex_array.hpp +++ b/include/boost/stacktrace/detail/to_hex_array.hpp @@ -1,11 +1,11 @@ -// Copyright Antony Polukhin, 2016. +// Copyright Antony Polukhin, 2016-2017. // // 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_STACKTRACE_TO_HEX_ARRAY_HPP -#define BOOST_STACKTRACE_DETAIL_STACKTRACE_TO_HEX_ARRAY_HPP +#ifndef BOOST_STACKTRACE_DETAIL_TO_HEX_ARRAY_HPP +#define BOOST_STACKTRACE_DETAIL_TO_HEX_ARRAY_HPP #include #ifdef BOOST_HAS_PRAGMA_ONCE @@ -51,4 +51,4 @@ inline boost::array to_hex_array(const void* ad }}} // namespace boost::stacktrace::detail -#endif // BOOST_STACKTRACE_DETAIL_STACKTRACE_TO_HEX_ARRAY_HPP +#endif // BOOST_STACKTRACE_DETAIL_TO_HEX_ARRAY_HPP diff --git a/include/boost/stacktrace/detail/try_demangle.hpp b/include/boost/stacktrace/detail/try_demangle.hpp new file mode 100644 index 0000000..501e40a --- /dev/null +++ b/include/boost/stacktrace/detail/try_demangle.hpp @@ -0,0 +1,35 @@ +// Copyright Antony Polukhin, 2016-2017. +// +// 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_TRY_DEMANGLE_HPP +#define BOOST_STACKTRACE_DETAIL_TRY_DEMANGLE_HPP + +#include +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include + +namespace boost { namespace stacktrace { namespace detail { + +inline std::string try_demangle(const char* mangled) { + std::string res; + + boost::core::scoped_demangled_name demangled(mangled); + if (demangled.get()) { + res = demangled.get(); + } else { + res = mangled; + } + + return res; +} + + +}}} // namespace boost::stacktrace::detail + +#endif // BOOST_STACKTRACE_DETAIL_TRY_DEMANGLE_HPP diff --git a/include/boost/stacktrace/detail/unwind_base_impls.hpp b/include/boost/stacktrace/detail/unwind_base_impls.hpp new file mode 100644 index 0000000..fc1cce9 --- /dev/null +++ b/include/boost/stacktrace/detail/unwind_base_impls.hpp @@ -0,0 +1,46 @@ +// Copyright Antony Polukhin, 2016-2017. +// +// 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_UNWIND_BASE_IMPLS_HPP +#define BOOST_STACKTRACE_DETAIL_UNWIND_BASE_IMPLS_HPP + +#include +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include + +namespace boost { namespace stacktrace { namespace detail { + +struct to_string_using_nothing { + void prepare_function_name(const void* addr) { + res = boost::stacktrace::frame(addr).name(); + } + + bool prepare_source_location() const BOOST_NOEXCEPT { + return false; + } +}; + +template class to_string_impl_base; +typedef to_string_impl_base to_string_impl; + +inline std::string name_impl(const void* /*addr*/) { + return std::string(); +} + +std::string frame::source_file() const { + return std::string(); +} + +std::size_t frame::source_line() const { + return 0; +} + +}}} // namespace boost::stacktrace::detail + +#endif // BOOST_STACKTRACE_DETAIL_UNWIND_BASE_IMPLS_HPP