diff --git a/CMakeLists.txt b/CMakeLists.txt index c5ed73395..90f7bfa31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,6 +221,7 @@ set( fc_sources src/asio.cpp src/string.cpp src/shared_ptr.cpp + src/stacktrace.cpp src/time.cpp src/utf8.cpp src/io/iostream.cpp diff --git a/include/fc/stacktrace.hpp b/include/fc/stacktrace.hpp new file mode 100644 index 000000000..1918b0cbb --- /dev/null +++ b/include/fc/stacktrace.hpp @@ -0,0 +1,16 @@ +// stacktrace.h (c) 2008, Timo Bingmann from http://idlebox.net/ +// published under the WTFPL v2.0 + +// Downloaded from http://panthema.net/2008/0901-stacktrace-demangled/ +// and modified for C++ and FC by Steemit, Inc. + +#pragma once + +#include + +namespace fc { + +void print_stacktrace(std::ostream& out); +void print_stacktrace_on_segfault(); + +} diff --git a/src/stacktrace.cpp b/src/stacktrace.cpp new file mode 100644 index 000000000..2504bb10e --- /dev/null +++ b/src/stacktrace.cpp @@ -0,0 +1,44 @@ +// +// A stacktrace handler for bitshares +// +#include + +// only include stacktrace stuff if boost >= 1.65 +#if BOOST_VERSION / 100000 >= 1 && ((BOOST_VERSION / 100) % 1000) >= 65 +#include +#include +#include + +namespace fc +{ + +static void segfault_signal_handler(int signum) +{ + ::signal(signum, SIG_DFL); + std::stringstream ss; + ss << boost::stacktrace::stacktrace(); + elog(ss.str()); + ::raise(SIGABRT); +} + +void print_stacktrace_on_segfault() +{ + ::signal(SIGSEGV, &segfault_signal_handler); +} + +void print_stacktrace(std::ostream& out) +{ + out << boost::stacktrace::stacktrace(); +} + +} +#else +// Stacktrace output requires Boost 1.65 or above. +// Therefore calls to these methods do nothing. +namespace fc +{ +void print_stacktrace_on_segfault() {} +void print_stacktrace(std::ostream& out) {} +} + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b1d582112..f2c058082 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -57,6 +57,7 @@ add_executable( all_tests all_tests.cpp bloom_test.cpp real128_test.cpp serialization_test.cpp + stacktrace_test.cpp time_test.cpp utf8_test.cpp variant_test.cpp diff --git a/tests/stacktrace_test.cpp b/tests/stacktrace_test.cpp new file mode 100644 index 000000000..baafa7669 --- /dev/null +++ b/tests/stacktrace_test.cpp @@ -0,0 +1,50 @@ +#include + +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(fc_stacktrace) + +BOOST_AUTO_TEST_CASE(stacktrace_test) +{ + // print the stack trace + std::stringstream ss; + fc::print_stacktrace(ss); + std::string results = ss.str(); +#if BOOST_VERSION / 100000 >= 1 && ((BOOST_VERSION / 100) % 1000) >= 65 + BOOST_CHECK(!results.empty()); + BOOST_CHECK(results.find("fc::print_stacktrace") != std::string::npos); +#else + BOOST_CHECK(results.empty()); +#endif +} + +BOOST_AUTO_TEST_CASE(threaded_stacktrace_test) +{ + fc::thread test_thread("a_thread"); + std::string results = test_thread.async( + [] ()->std::string { + // cause a pause + for(int i = 0; i < 10000; i++); + std::stringstream ss; + fc::print_stacktrace(ss); + return ss.str(); + } + ).wait(); +#if BOOST_VERSION / 100000 >= 1 && ((BOOST_VERSION / 100) % 1000) >= 65 + BOOST_CHECK(!results.empty()); + BOOST_CHECK(results.find("fc::print_stacktrace") != std::string::npos); +#else + BOOST_CHECK(results.empty()); +#endif +} + +/* this test causes a segfault on purpose to test the event handler +BOOST_AUTO_TEST_CASE(cause_segfault) +{ + fc::print_stacktrace_on_segfault(); + ::raise(SIGSEGV); +} +*/ +BOOST_AUTO_TEST_SUITE_END()