diff options
Diffstat (limited to 'boost/stacktrace/detail/addr2line_impls.hpp')
-rw-r--r-- | boost/stacktrace/detail/addr2line_impls.hpp | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/boost/stacktrace/detail/addr2line_impls.hpp b/boost/stacktrace/detail/addr2line_impls.hpp new file mode 100644 index 0000000000..e314fe9f31 --- /dev/null +++ b/boost/stacktrace/detail/addr2line_impls.hpp @@ -0,0 +1,225 @@ +// 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 <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/stacktrace/detail/to_hex_array.hpp> +#include <boost/core/demangle.hpp> +#include <boost/lexical_cast.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::conversion::try_lexical_convert(res, line_num)) { + return 0; + } + + return line_num; +} + + +}} // namespace boost::stacktrace + +#endif // BOOST_STACKTRACE_DETAIL_ADDR2LINE_IMPLS_HPP |