Skip to content

Commit 276e7f0

Browse files
committed
Switch from libunwind to boost::stacktrace
1 parent 6286293 commit 276e7f0

9 files changed

Lines changed: 73 additions & 254 deletions

File tree

.circleci/config.yml

Lines changed: 9 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -86,60 +86,16 @@ references:
8686
cmake --version
8787
/usr/local/bin/$CC --version
8888
/usr/local/bin/$CXX --version
89-
upgrade_libunwind_pre: &upgrade_libunwind_pre
90-
restore_cache:
91-
keys:
92-
# Find the most recent cache from any branch
93-
- v1_upgrade_libunwind_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
94-
upgrade_libunwind_post: &upgrade_libunwind_post
95-
save_cache:
96-
key: v1_upgrade_libunwind_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
97-
paths:
98-
- /tmp/libunwind-1.2.1
99-
upgrade_libunwind: &upgrade_libunwind
100-
run:
101-
name: Upgrade Libunwind
102-
command: |
103-
# We need to install libunwind manually because Circle CI uses Ubuntu 14.04 and the default libunwind version
104-
# on that Ubuntu suffers from http://savannah.nongnu.org/bugs/?43752.
105-
# This isn't an issue for any later version of Ubuntu.
106-
107-
# Detect number of CPU cores
108-
export NUMCORES=`nproc`
109-
echo Using $NUMCORES cores
110-
# Download and prepare libunwind (only if not already present from cache)
111-
if [ ! -d "/tmp/libunwind-1.2.1" ]; then
112-
echo "Didn't find libunwind in cache. Downloading and building."
113-
wget -O /tmp/libunwind-1.2.1.tar.gz http://download.savannah.nongnu.org/releases/libunwind/libunwind-1.2.1.tar.gz
114-
if [ $(sha512sum /tmp/libunwind-1.2.1.tar.gz | awk '{print $1;}') == "af7c280d2a963779a4a2711887618bc96383011e4e5d52e4085aa7fb351e55e357468f6ff85e66a216f1c6826538f498335a917a5970575c93be74c96316319b" ]; then
115-
echo Correct sha512sum
116-
else
117-
echo Wrong sha512sum
118-
sha512sum /tmp/libunwind-1.2.1.tar.gz
119-
exit 1
120-
fi
121-
echo Extracting...
122-
tar -xf /tmp/libunwind-1.2.1.tar.gz -C /tmp
123-
rm -rf /tmp/libunwind-1.2.1.tar.gz
124-
cd /tmp/libunwind-1.2.1
125-
./configure
126-
make -j${NUMCORES}
127-
else
128-
echo Found libunwind in cache. Use cache and build.
129-
fi
130-
# Compile and install libunwind (if cached, this should be fast)
131-
cd /tmp/libunwind-1.2.1
132-
sudo make -j${NUMCORES} install
13389
upgrade_boost_pre: &upgrade_boost_pre
13490
restore_cache:
13591
keys:
13692
# Find the most recent cache from any branch
137-
- v3_upgrade_boost_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
93+
- v4_upgrade_boost_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
13894
upgrade_boost_post: &upgrade_boost_post
13995
save_cache:
140-
key: v3_upgrade_boost_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
96+
key: v4_upgrade_boost_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
14197
paths:
142-
- /tmp/boost_1_57_0
98+
- /tmp/boost_1_65_1
14399
upgrade_boost: &upgrade_boost
144100
run:
145101
name: Upgrade Boost
@@ -148,10 +104,10 @@ references:
148104
export NUMCORES=`nproc`
149105
echo Using $NUMCORES cores
150106
# Download and prepare boost (only if not already present from cache)
151-
if [ ! -d "/tmp/boost_1_57_0" ]; then
107+
if [ ! -d "/tmp/boost_1_65_1" ]; then
152108
echo "Didn't find boost in cache. Downloading and building."
153-
wget -O /tmp/boost.tar.bz2 https://sourceforge.net/projects/boost/files/boost/1.57.0/boost_1_57_0.tar.bz2/download
154-
if [ $(sha512sum /tmp/boost.tar.bz2 | awk '{print $1;}') == "61881440fd89644c43c6e3bc6292e9fed75a6d3a76f98654b189d0ed4e1087d77b585884e882270c08bf9f7132b173bfc1fde05848e06aa78ba7f1008d10714d" ]; then
109+
wget -O /tmp/boost.tar.bz2 https://sourceforge.net/projects/boost/files/boost/1.65.1/boost_1_65_1.tar.bz2/download
110+
if [ $(sha512sum /tmp/boost.tar.bz2 | awk '{print $1;}') == "a9e6866d3bb3e7c198f442ff09f5322f58064dca79bc420f2f0168eb63964226dfbc4f034a5a5e5958281fdf7518a1b057c894fbda0b61fced59c1661bf30f1a" ]; then
155111
echo Correct sha512sum
156112
else
157113
echo Wrong sha512sum
@@ -161,14 +117,14 @@ references:
161117
echo Extracting...
162118
tar -xf /tmp/boost.tar.bz2 -C /tmp
163119
rm -rf boost.tar.bz2
164-
cd /tmp/boost_1_57_0
120+
cd /tmp/boost_1_65_1
165121
./bootstrap.sh --with-toolset=${BUILD_TOOLSET} --with-libraries=filesystem,thread,chrono,program_options
166122
cd ..
167123
else
168124
echo Found boost in cache. Use cache and build.
169125
fi
170126
# Compile and install boost (if cached, this should be fast)
171-
cd /tmp/boost_1_57_0
127+
cd /tmp/boost_1_65_1
172128
sudo ./b2 toolset=${BUILD_TOOLSET} link=static cxxflags=-fPIC -d0 -j$NUMCORES install
173129
build_pre: &build_pre
174130
restore_cache:
@@ -233,9 +189,6 @@ references:
233189
- <<: *container_setup_pre
234190
- <<: *container_setup
235191
- <<: *container_setup_post
236-
- <<: *upgrade_libunwind_pre
237-
- <<: *upgrade_libunwind
238-
- <<: *upgrade_libunwind_post
239192
- <<: *upgrade_boost_pre
240193
- <<: *upgrade_boost
241194
- <<: *upgrade_boost_post
@@ -514,7 +467,7 @@ jobs:
514467
OMP_NUM_THREADS: "1"
515468
CXXFLAGS: "-O2 -fsanitize=thread -fno-omit-frame-pointer"
516469
BUILD_TYPE: "Debug"
517-
GTEST_ARGS: "--gtest_filter=-LoggingTest.LoggingAlsoWorksAfterFork:AssertTest_DebugBuild.*:SignalCatcherTest.*_thenDies:SignalHandlerTest.*_thenDies:SignalHandlerTest.givenMultipleSigIntHandlers_whenRaising_thenCatchesCorrectSignal:CliTest_Setup.*:CliTest_IntegrityCheck.*:*/CliTest_WrongEnvironment.*:CliTest_Unmount.*"
470+
GTEST_ARGS: "--gtest_filter=-LoggingTest.LoggingAlsoWorksAfterFork:AssertTest_*:BacktraceTest.*:SignalCatcherTest.*_thenDies:SignalHandlerTest.*_thenDies:SignalHandlerTest.givenMultipleSigIntHandlers_whenRaising_thenCatchesCorrectSignal:CliTest_Setup.*:CliTest_IntegrityCheck.*:*/CliTest_WrongEnvironment.*:CliTest_Unmount.*"
518471
CMAKE_FLAGS: ""
519472
RUN_TESTS: true
520473
clang_tidy:
@@ -524,9 +477,6 @@ jobs:
524477
- <<: *container_setup_pre
525478
- <<: *container_setup
526479
- <<: *container_setup_post
527-
- <<: *upgrade_libunwind_pre
528-
- <<: *upgrade_libunwind
529-
- <<: *upgrade_libunwind_post
530480
- <<: *upgrade_boost_pre
531481
- <<: *upgrade_boost
532482
- <<: *upgrade_boost_post

ChangeLog.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Fixed bugs:
77

88
Compatibility:
99
* Fixed some incompatibilities with systems using the musl libc
10-
* Use libunwind instead of libbacktrace to build stack traces. This fixes a segfault issue with platforms using libexecinfo and is generally more portable.
10+
* Use boost::stacktrace instead of libbacktrace to build stack traces. This fixes a segfault issue with platforms using libexecinfo and is generally more portable.
1111

1212
Other:
1313
* Updated to crypto++ 8.1

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Requirements
4646
- GCC version >= 5.0 or Clang >= 4.0
4747
- CMake version >= 3.0
4848
- libcurl4 (including development headers)
49-
- Boost libraries version >= 1.57 (including development headers)
49+
- Boost libraries version >= 1.65.1 (including development headers)
5050
- filesystem
5151
- system
5252
- chrono
@@ -56,15 +56,14 @@ Requirements
5656
- libFUSE version >= 2.8.6 (including development headers), on Mac OS X instead install osxfuse from https://osxfuse.github.io/
5757
- Python >= 2.7
5858
- OpenMP
59-
- Libunwind
6059

6160
You can use the following commands to install these requirements
6261

6362
# Ubuntu
64-
$ sudo apt install git g++ cmake make libcurl4-openssl-dev libboost-filesystem-dev libboost-system-dev libboost-chrono-dev libboost-program-options-dev libboost-thread-dev libssl-dev libfuse-dev python libunwind-dev
63+
$ sudo apt install git g++ cmake make libcurl4-openssl-dev libboost-filesystem-dev libboost-system-dev libboost-chrono-dev libboost-program-options-dev libboost-thread-dev libssl-dev libfuse-dev python
6564

6665
# Fedora
67-
sudo dnf install git gcc-c++ cmake make libcurl-devel boost-devel boost-static openssl-devel fuse-devel python libunwind-devel
66+
sudo dnf install git gcc-c++ cmake make libcurl-devel boost-devel boost-static openssl-devel fuse-devel python
6867

6968
# Macintosh
7069
brew install cmake boost openssl libomp

cmake-utils/utils.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,15 @@ endfunction(target_enable_style_warnings)
108108
function(target_add_boost TARGET)
109109
# Load boost libraries
110110
if(NOT DEFINED Boost_USE_STATIC_LIBS OR Boost_USE_STATIC_LIBS)
111-
# Many supported systems don't have boost >= 1.57. Better link it statically.
111+
# Many supported systems don't have boost >= 1.65.1. Better link it statically.
112112
message(STATUS "Boost will be statically linked")
113113
set(Boost_USE_STATIC_LIBS ON)
114114
else(NOT DEFINED Boost_USE_STATIC_LIBS OR Boost_USE_STATIC_LIBS)
115115
message(STATUS "Boost will be dynamically linked")
116116
set(Boost_USE_STATIC_LIBS OFF)
117117
endif(NOT DEFINED Boost_USE_STATIC_LIBS OR Boost_USE_STATIC_LIBS)
118118
set(BOOST_THREAD_VERSION 4)
119-
find_package(Boost 1.57.0
119+
find_package(Boost 1.65.1
120120
REQUIRED
121121
COMPONENTS ${ARGN})
122122
target_include_directories(${TARGET} SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})

src/cpp-utils/CMakeLists.txt

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,17 @@ add_library(${PROJECT_NAME} STATIC ${SOURCES})
6363

6464
if(MSVC)
6565
target_link_libraries(${PROJECT_NAME} PUBLIC DbgHelp)
66-
elseif(NOT APPLE)
67-
# note: We use the libunwind code on Apple, but we don't seem to need these lines to link against it.
68-
find_package(Libunwind REQUIRED)
69-
target_include_directories(${PROJECT_NAME} PRIVATE ${LIBUNWIND_INCLUDE_DIR})
70-
target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBUNWIND_LIBRARIES})
71-
target_compile_definitions(${PROJECT_NAME} PRIVATE ${LIBUNWIND_DEFINITIONS})
66+
elseif (APPLE)
67+
target_compile_definitions(${PROJECT_NAME} PRIVATE BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED)
68+
else()
69+
find_program(ADDR2LINE addr2line)
70+
if ("${ADDR2LINE}" STREQUAL "ADDR2LINE-NOTFOUND")
71+
message(WARNING "addr2line not found. Backtraces will be reduced.")
72+
else()
73+
message(STATUS "addr2line found. Using it for backtraces.")
74+
target_compile_definitions(${PROJECT_NAME} PRIVATE BOOST_STACKTRACE_USE_ADDR2LINE)
75+
target_compile_definitions(${PROJECT_NAME} PRIVATE BOOST_STACKTRACE_ADDR2LINE_LOCATION=${ADDR2LINE})
76+
endif()
7277
endif()
7378

7479
if (NOT MSVC)
Lines changed: 25 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,49 @@
11
#if !defined(_MSC_VER)
22

33
#include <csignal>
4-
#include <cxxabi.h>
54
#include <sstream>
65

76
#include "../logging/logging.h"
87
#include <cpp-utils/process/SignalHandler.h>
98

10-
#define UNW_LOCAL_ONLY
11-
#include <libunwind.h>
12-
13-
// TODO Add file and line number on non-windows
9+
#include <boost/stacktrace.hpp>
1410

1511
using std::string;
1612
using std::ostringstream;
1713
using namespace cpputils::logging;
1814

1915
namespace cpputils {
2016

21-
namespace {
22-
std::string demangle(const string &mangledName) {
23-
string result;
24-
int status = -10;
25-
char *demangledName = nullptr;
26-
try {
27-
demangledName = abi::__cxa_demangle(mangledName.c_str(), NULL, NULL, &status);
28-
if (status == 0) {
29-
result = demangledName;
30-
} else if (status == -2) {
31-
// mangledName was not a c++ mangled name, probably because it's a C name like for static
32-
// initialization or stuff. Let's just return the name instead.
33-
result = mangledName;
34-
} else {
35-
// other error
36-
result = "[demangling error " + std::to_string(status) + "]" + mangledName;
37-
}
38-
free(demangledName);
39-
return result;
40-
} catch (...) {
41-
free(demangledName);
42-
throw;
43-
}
44-
}
45-
46-
void pretty_print(std::ostringstream& str, unw_cursor_t* cursor) {
47-
constexpr unsigned int MAXNAMELEN=256;
48-
char name[MAXNAMELEN];
49-
unw_word_t offp = 0, ip = 0;
50-
51-
int status = unw_get_reg(cursor, UNW_REG_IP, &ip);
52-
if (0 != status) {
53-
str << "[unw_get_reg error: " << status << "]: ";
54-
} else {
55-
str << "0x" << std::hex << ip << ": ";
56-
}
57-
58-
status = unw_get_proc_name(cursor, name, MAXNAMELEN, &offp);
59-
if (0 != status) {
60-
str << "[unw_get_proc_name error: " << status << "]";
61-
} else {
62-
str << demangle(name);
63-
}
64-
str << " +0x" << std::hex << offp;
65-
}
17+
string backtrace() {
18+
std::ostringstream str;
19+
str << boost::stacktrace::stacktrace();
20+
return str.str();
6621
}
6722

68-
string backtrace() {
69-
std::ostringstream result;
70-
71-
unw_context_t uc;
72-
int status = unw_getcontext(&uc);
73-
if (0 != status) {
74-
return "[unw_getcontext error: " + std::to_string(status) + "]";
75-
}
76-
77-
unw_cursor_t cursor;
78-
status = unw_init_local(&cursor, &uc);
79-
if (0 != status) {
80-
return "[unw_init_local error: " + std::to_string(status) + "]";
81-
}
82-
83-
84-
size_t line = 0;
85-
while ((status = unw_step(&cursor)) > 0) {
86-
result << "#" << std::dec << (line++) << " ";
87-
pretty_print(result, &cursor);
88-
result << "\n";
89-
}
90-
if (status != 0) {
91-
result << "[unw_step error :" << status << "]";
92-
}
93-
94-
return result.str();
95-
}
96-
9723
namespace {
98-
void sigsegv_handler(int) {
99-
LOG(ERR, "SIGSEGV\n{}", backtrace());
100-
exit(1);
101-
}
102-
void sigill_handler(int) {
103-
LOG(ERR, "SIGILL\n{}", backtrace());
104-
exit(1);
105-
}
106-
void sigabrt_handler(int) {
107-
LOG(ERR, "SIGABRT\n{}", backtrace());
108-
exit(1);
109-
}
24+
void sigsegv_handler(int) {
25+
LOG(ERR, "SIGSEGV\n{}", backtrace());
26+
exit(1);
27+
}
28+
void sigill_handler(int) {
29+
LOG(ERR, "SIGILL\n{}", backtrace());
30+
exit(1);
31+
}
32+
void sigabrt_handler(int) {
33+
LOG(ERR, "SIGABRT\n{}", backtrace());
34+
exit(1);
35+
}
11036
}
11137

112-
void showBacktraceOnCrash() {
113-
// the signal handler RAII objects will be initialized on first call (which will register the signal handler)
114-
// and destroyed on program exit (which will unregister the signal handler)
38+
void showBacktraceOnCrash() {
39+
// the signal handler RAII objects will be initialized on first call (which will register the signal handler)
40+
// and destroyed on program exit (which will unregister the signal handler)
41+
42+
static SignalHandlerRAII<&sigsegv_handler> segv(SIGSEGV);
43+
static SignalHandlerRAII<&sigabrt_handler> abrt(SIGABRT);
44+
static SignalHandlerRAII<&sigill_handler> ill(SIGILL);
45+
}
11546

116-
static SignalHandlerRAII<&sigsegv_handler> segv(SIGSEGV);
117-
static SignalHandlerRAII<&sigabrt_handler> abrt(SIGABRT);
118-
static SignalHandlerRAII<&sigill_handler> ill(SIGILL);
119-
}
12047
}
12148

12249
#endif

0 commit comments

Comments
 (0)