| // 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_ADDR2LINE_IMPLS_HPP |
| #define BOOST_STACKTRACE_DETAIL_ADDR2LINE_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/try_dec_convert.hpp> |
| #include <boost/core/demangle.hpp> |
| #include <cstdio> |
| |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <signal.h> |
| |
| |
| namespace boost { namespace stacktrace { namespace detail { |
| |
| |
| #if defined(BOOST_STACKTRACE_ADDR2LINE_LOCATION) && !defined(BOOST_NO_CXX11_CONSTEXPR) |
| |
| constexpr bool is_abs_path(const char* path) BOOST_NOEXCEPT { |
| return *path != '\0' && ( |
| *path == ':' || *path == '/' || is_abs_path(path + 1) |
| ); |
| } |
| |
| #endif |
| |
| 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]; |
| #ifdef BOOST_STACKTRACE_ADDR2LINE_LOCATION |
| char prog_name[] = BOOST_STRINGIZE( BOOST_STACKTRACE_ADDR2LINE_LOCATION ); |
| #if !defined(BOOST_NO_CXX11_CONSTEXPR) && !defined(BOOST_NO_CXX11_STATIC_ASSERT) |
| static_assert( |
| boost::stacktrace::detail::is_abs_path( BOOST_STRINGIZE( BOOST_STACKTRACE_ADDR2LINE_LOCATION ) ), |
| "BOOST_STACKTRACE_ADDR2LINE_LOCATION must be an absolute path" |
| ); |
| #endif |
| |
| #else |
| char prog_name[] = "/usr/bin/addr2line"; |
| #endif |
| |
| char* argp[] = { |
| prog_name, |
| const_cast<char*>(flag), |
| const_cast<char*>(exec_path), |
| const_cast<char*>(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); |
| } |
| |
| // Do not use `execlp()`, `execvp()`, and `execvpe()` here! |
| // `exec*p*` functions are vulnerable to PATH variable evaluation attacks. |
| ::execv(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; |
| |
| boost::stacktrace::detail::location_from_symbol loc(addr); |
| if (!loc.empty()) { |
| res = loc.name(); |
| } else { |
| res.resize(16); |
| int rlin_size = ::readlink("/proc/self/exe", &res[0], res.size() - 1); |
| while (rlin_size == static_cast<int>(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 Base> class to_string_impl_base; |
| typedef to_string_impl_base<to_string_using_addr2line> 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::core::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::stacktrace::detail::try_dec_convert(res.c_str(), line_num)) { |
| return 0; |
| } |
| |
| return line_num; |
| } |
| |
| |
| }} // namespace boost::stacktrace |
| |
| #endif // BOOST_STACKTRACE_DETAIL_ADDR2LINE_IMPLS_HPP |