Skip to content

Commit

Permalink
Use libunwind for stack unwinding
Browse files Browse the repository at this point in the history
The backtrace() interface is not available on Alpine. ([#245])
  • Loading branch information
ksedgwic committed Oct 28, 2024
1 parent f32426d commit 5a546c1
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 8 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ jobs:
automake \
curl \
libcurl4-openssl-dev \
libunwind \
libunwind-dev \
git \
libev-dev \
libtool \
Expand Down
4 changes: 2 additions & 2 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -571,8 +571,8 @@ EXTRA_DIST = \
generate_commit_hash.sh \
commit_hash.h

AM_CXXFLAGS = -Wall -Werror $(PTHREAD_CFLAGS) $(libev_CFLAGS) $(SQLITE3_CFLAGS) $(CURL_CFLAGS) $(CLBOSS_CXXFLAGS)
LDADD = libclboss.la $(PTHREAD_LIBS) $(libev_LIBS) $(SQLITE3_LIBS) $(CURL_LIBS)
AM_CXXFLAGS = -Wall -Werror $(PTHREAD_CFLAGS) $(libev_CFLAGS) $(SQLITE3_CFLAGS) $(CURL_CFLAGS) $(CLBOSS_CXXFLAGS) $(LIBUNWIND_CFLAGS)
LDADD = libclboss.la $(PTHREAD_LIBS) $(libev_LIBS) $(SQLITE3_LIBS) $(CURL_LIBS) $(LIBUNWIND_LIBS)

ACLOCAL_AMFLAGS = -I m4

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,23 @@ Debian-derived systems:
* `libev-dev`
* `libcurl4-gnutls-dev`
* `libsqlite3-dev`
* `libunwind-dev`

RPM-dervied :
* `groupinstall "Development Tools"`
* `pkg-config`
* `libev-devel`
* `libcurl-devel`
* `libsqlite3x-devel`
* `libunwind-devel`

Alpine:
* `build-base`
* `pkgconf`
* `libev-dev`
* `curl-dev`
* `sqlite-dev`
* `libunwind-dev`

Equivalent packages have a good probability of existing in
non-Debian-derived distributions as well.
Expand Down Expand Up @@ -132,6 +135,7 @@ release:
pkg install libev
pkg install pkgconf
pkg install sqlite3
pkg install libunwind

In addition, you have to use `gmake` for building, not the
system `make`, as the included `libsecp256k1` requires
Expand Down
33 changes: 27 additions & 6 deletions Util/BacktraceException.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>

#ifdef HAVE_CONFIG_H
# include"config.h"
#endif

extern std::string g_argv0;

#define UNW_LOCAL_ONLY
#include <libunwind.h>

namespace Util {

/** class Util::BacktraceException<E>
Expand Down Expand Up @@ -54,23 +58,40 @@ class BacktraceException : public T {
static constexpr size_t MAX_FRAMES = 100;
mutable bool formatted_ = false;
mutable std::string full_message_;
mutable void* backtrace_addresses_[MAX_FRAMES];
mutable std::vector<unw_word_t> backtrace_addresses_;
mutable size_t stack_depth;

void capture_backtrace() {
memset(backtrace_addresses_, 0, sizeof(backtrace_addresses_));
stack_depth = backtrace(backtrace_addresses_, sizeof(backtrace_addresses_) / sizeof(void*));
unw_cursor_t cursor;
unw_context_t context;
unw_getcontext(&context);
unw_init_local(&cursor, &context);

while (unw_step(&cursor) > 0 && backtrace_addresses_.size() < MAX_FRAMES) {
unw_word_t ip;
unw_get_reg(&cursor, UNW_REG_IP, &ip);
backtrace_addresses_.push_back(ip);
}

stack_depth = backtrace_addresses_.size();
}

std::string format_backtrace() const {
char** symbols = backtrace_symbols(backtrace_addresses_, stack_depth);
// Create an array of pointers compatible with `backtrace_symbols`.
std::vector<void*> pointers(backtrace_addresses_.size());
for (size_t i = 0; i < backtrace_addresses_.size(); ++i) {
pointers[i] = reinterpret_cast<void*>(backtrace_addresses_[i]);
}

// Use `backtrace_symbols` with the adapted pointers.
char** symbols = backtrace_symbols(pointers.data(), stack_depth);
std::ostringstream oss;
for (size_t i = 0; i < stack_depth; ++i) {
oss << '#' << std::left << std::setfill(' ') << std::setw(2) << i << ' ';
auto line = addr2line(backtrace_addresses_[i]);
auto line = addr2line(pointers[i]);
if (line.find("??") != std::string::npos) {
// If addr2line doesn't find a good
// answer use basic information
// answer, use basic information
// instead.
oss << symbols[i] << std::endl;
} else {
Expand Down
16 changes: 16 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ AX_LIB_CURL(,[:],[
AC_MSG_ERROR([Need libcurl.])
])

# Check for libunwind
PKG_CHECK_MODULES([LIBUNWIND], [libunwind], [
AC_DEFINE([HAVE_LIBUNWIND], [1], [Define if libunwind is available])
], [
AC_MSG_ERROR([libunwind is required but not found])
])

# https://github.com/ZmnSCPxj/clboss/issues/245
case "$host_os" in
*alpine*)
LDFLAGS="$LDFLAGS -lexecinfo"
;;
esac

AC_SUBST(LDFLAGS)

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.
Expand Down
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
libev
libevdev
curl
libunwind
sqlite
bind
autoconf
Expand Down

0 comments on commit 5a546c1

Please sign in to comment.