Separate code for addr2line, libbacktrace and basic

This commit is contained in:
Antony Polukhin
2017-01-13 22:28:53 +03:00
parent c5843350f0
commit 3ec1c49c3b
6 changed files with 441 additions and 302 deletions

View File

@@ -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 <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/stacktrace/detail/to_hex_array.hpp>
#include <boost/stacktrace/detail/try_demangle.hpp>
#include <boost/lexical_cast.hpp>
#include <dlfcn.h>
#include <execinfo.h>
#include <cstdio>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
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<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);
}
::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<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::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

View File

@@ -12,170 +12,26 @@
# pragma once
#endif
#include <boost/core/demangle.hpp>
#include <boost/stacktrace/detail/to_hex_array.hpp>
#include <boost/stacktrace/detail/try_demangle.hpp>
#include <boost/lexical_cast.hpp>
#include <unwind.h>
#ifdef BOOST_STACKTRACE_USE_BACKTRACE
#include <backtrace.h>
#endif
#include <dlfcn.h>
#include <execinfo.h>
#include <cstdio>
#ifdef BOOST_STACKTRACE_USE_ADDR2LINE
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#ifdef BOOST_STACKTRACE_USE_BACKTRACE
# include <boost/stacktrace/detail/libbacktrace_impls.hpp>
#elif defined(BOOST_STACKTRACE_USE_ADDR2LINE)
# include <boost/stacktrace/detail/addr2line_impls.hpp>
#else
# include <boost/stacktrace/detail/unwind_base_impls.hpp>
#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<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);
}
::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<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;
}
#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<pc_data*>(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 Base>
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<uintptr_t>(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<boost::array<char, 40> >(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<boost::array<char, 40> >(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<uintptr_t>(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<uintptr_t>(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<uintptr_t>(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

View File

@@ -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 <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/stacktrace/detail/to_hex_array.hpp>
#include <boost/stacktrace/detail/try_demangle.hpp>
#include <boost/lexical_cast.hpp>
#include <backtrace.h>
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<pc_data*>(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<uintptr_t>(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<boost::array<char, 40> >(line).data();
return true;
}
to_string_using_backtrace() BOOST_NOEXCEPT {
state = ::backtrace_create_state(
0, 0, 0, 0
);
}
};
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;
::backtrace_state* state = ::backtrace_create_state(
0, 0, 0, 0
);
boost::stacktrace::detail::pc_data data = {&res, 0, 0};
::backtrace_pcinfo(state, reinterpret_cast<uintptr_t>(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<uintptr_t>(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<uintptr_t>(addr_), boost::stacktrace::detail::libbacktrace_full_callback, 0, &data);
return data.line;
}
}} // namespace boost::stacktrace
#endif // BOOST_STACKTRACE_DETAIL_LIBBACKTRACE_IMPLS_HPP

View File

@@ -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 <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
@@ -51,4 +51,4 @@ inline boost::array<char, 2 + sizeof(void*) * 2 + 1> 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

View File

@@ -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 <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/core/demangle.hpp>
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

View File

@@ -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 <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/stacktrace/frame.hpp>
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 Base> class to_string_impl_base;
typedef to_string_impl_base<to_string_using_nothing> 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