From b7c8f76765f25ce21c3fd2bdd769bb2e9b608458 Mon Sep 17 00:00:00 2001 From: Matthias Hochsteger Date: Mon, 23 Sep 2019 14:01:35 +0200 Subject: [PATCH 1/2] Print stack trace on RangeException --- libsrc/core/CMakeLists.txt | 1 + libsrc/core/exception.cpp | 172 +++++++++++++++++++++++++++++++++++++ libsrc/core/exception.hpp | 5 ++ 3 files changed, 178 insertions(+) create mode 100644 libsrc/core/exception.cpp diff --git a/libsrc/core/CMakeLists.txt b/libsrc/core/CMakeLists.txt index 0760ce6c..e18dd87d 100644 --- a/libsrc/core/CMakeLists.txt +++ b/libsrc/core/CMakeLists.txt @@ -2,6 +2,7 @@ add_library(ngcore SHARED archive.cpp bitarray.cpp + exception.cpp localheap.cpp logging.cpp flags.cpp diff --git a/libsrc/core/exception.cpp b/libsrc/core/exception.cpp new file mode 100644 index 00000000..63de7d7f --- /dev/null +++ b/libsrc/core/exception.cpp @@ -0,0 +1,172 @@ +#include "exception.hpp" +#include "utils.hpp" + +#ifdef __GNUC__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ngcore +{ + namespace detail + { + static int exec(std::string cmd, std::string & out) { + std::array buffer; + FILE *pipe = popen(cmd.c_str(), "r"); + + if (!pipe) + throw std::runtime_error("popen() failed!"); + + out = ""; + while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) + out += buffer.data(); + + int error_code = pclose(pipe); + return error_code; + } + +#ifdef __APPLE__ + // Split output line from backtrace_symbols to recover function name and offset + // then use `nm` command line tool to get the address of the function + // then use `add42line` command line tool to map function address + offset to line in source code + static std::string TranslateBacktrace( std::string s, std::string libname ) + { + constexpr char reset_shell[] = "\033[0m"; + constexpr char green[] = "\033[32m"; + constexpr char yellow[] = "\033[33m"; + + std::istringstream in(s); + + std::string libname1, funcname, addr, plus_sign; + size_t i,offset; + + in >> i >> libname1 >> addr >> funcname >> plus_sign >> std::hex >> offset; + + std::stringstream out; + + if(!funcname.empty() && !libname.empty()) + { + std::array buffer; + int status; + size_t size = buffer.size(); + abi::__cxa_demangle(funcname.c_str(), buffer.data(), &size, &status); + out << "in " << yellow << buffer.data() << reset_shell; + + std::string nm_command = "nm " + libname + " | grep \"" + funcname + "$\" | cut -f 1 -d ' '"; + std::output; + auto exit_code = exec(nm_command, output); + auto fptr = std::strtoul(output.c_str(), 0, 16); + if(fptr == 0) + return out.str()+'\n'; + std::stringstream offset_s; + offset_s << "0x" << std::hex << fptr+offset - 5; + std::string addr2line_command = std::string("atos -o ") + libname + " --fullPath " + offset_s.str(); + exit_code = exec(addr2line_command, output); + if(exit_code==0) + out << " at " << green << output << reset_shell; + else + out << '\n'; + } + else + out << s << '\n'; + + return out.str(); + } +#else // __APPLE__ + + // Split output line from backtrace_symbols to recover function name and offset + // then use `nm` command line tool to get the address of the function + // then use `addr2line` command line tool to map function address + offset to line in source code + static std::string TranslateBacktrace( std::string s, std::string /*dummy*/ ) + { + // example line: + // /home/mhochsteger/install/ngs_clang/bin/../lib/libngcore.so(_ZN6ngcore11TaskManager4LoopEi+0x1e0) [0x7f2991fe1030] + constexpr char reset_shell[] = "\033[0m"; + constexpr char green[] = "\033[32m"; + constexpr char yellow[] = "\033[33m"; + + auto brace_open_pos = s.find('('); + auto brace_close_pos = s.find(')', brace_open_pos); + auto plus_pos = s.find('+', brace_open_pos); + auto bracket_open_pos = s.find('['); + auto bracket_close_pos = s.find(']'); + + auto libname = s.substr(0, brace_open_pos); + auto funcname = s.substr(brace_open_pos+1, plus_pos - brace_open_pos - 1); + auto offset = std::strtoul(s.substr(plus_pos+1, brace_close_pos - plus_pos - 1).c_str(), 0, 16); + auto position = std::strtoul(s.substr(bracket_open_pos+1, bracket_close_pos - bracket_open_pos - 1).c_str(), 0, 16); + std::stringstream out; + + if(!funcname.empty()) + { + std::array buffer; + int status; + size_t size = buffer.size(); + abi::__cxa_demangle(funcname.c_str(), buffer.data(), &size, &status); + out << "in " << yellow << buffer.data() << reset_shell; + + std::string nm_command = "nm " + libname + " | grep " + funcname + " | cut -f 1 -d ' '"; + std::string output; + auto exit_code = exec(nm_command, output); + auto fptr = std::strtoul(output.c_str(), 0, 16); + + std::stringstream offset_s; + offset_s << "0x" << std::hex << fptr+offset - 5; + std::string addr2line_command = std::string("addr2line -e ") + libname + " " + offset_s.str(); + exit_code = exec(addr2line_command, output); + if(exit_code==0) + out << " at " << green << output << reset_shell; + else + out << '\n'; + } + else + out << s << '\n'; + + return out.str(); + } +#endif // __APPLE__ + + } // namespace detail + + std::string GetBackTrace() + { + std::stringstream result; + void *bt[1024]; + int bt_size; + char **bt_syms; + int i; + + bt_size = backtrace(bt, 1024); + bt_syms = backtrace_symbols(bt, bt_size); + Dl_info info; + for (i = 1; i < bt_size-1; i++) + { + dladdr(bt[i], &info); + size_t len = strlen(bt_syms[i]); + result << '#'<< i << '\t' << detail::TranslateBacktrace( bt_syms[i], info.dli_fname ); + } + free(bt_syms); + return result.str(); + } + +} // namespace ngcore + +#else // __GNUC__ + +namespace ngcore +{ + std::string GetBackTrace() + { + return std::string(); + } +} // namespace ngcore + +#endif // __GNUC__ diff --git a/libsrc/core/exception.hpp b/libsrc/core/exception.hpp index 68e7c073..406a4eb2 100644 --- a/libsrc/core/exception.hpp +++ b/libsrc/core/exception.hpp @@ -7,8 +7,12 @@ #include "ngcore_api.hpp" // for NGCORE_API + namespace ngcore { + + NGCORE_API std::string GetBackTrace(); + // Exception for code that shouldn't be executed class NGCORE_API UnreachableCodeException : public std::exception { @@ -57,6 +61,7 @@ namespace ngcore std::stringstream str; str << where << ": index " << ind << " out of range [" << imin << "," << imax << ")\n"; Append (str.str()); + Append (GetBackTrace()); } template From 9f7b56fd1f7e198754b6ba258be3aa4256e46890 Mon Sep 17 00:00:00 2001 From: Matthias Hochsteger Date: Tue, 1 Oct 2019 16:30:41 +0200 Subject: [PATCH 2/2] Register signal handlers to print stack trace --- libsrc/core/exception.cpp | 40 ++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/libsrc/core/exception.cpp b/libsrc/core/exception.cpp index 63de7d7f..816822b5 100644 --- a/libsrc/core/exception.cpp +++ b/libsrc/core/exception.cpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace ngcore { @@ -39,6 +40,8 @@ namespace ngcore // then use `add42line` command line tool to map function address + offset to line in source code static std::string TranslateBacktrace( std::string s, std::string libname ) { + // example line + // 1 libngcore.dylib 0x000000010ddb298c _ZL21ngcore_signal_handleri + 316 constexpr char reset_shell[] = "\033[0m"; constexpr char green[] = "\033[32m"; constexpr char yellow[] = "\033[33m"; @@ -54,14 +57,8 @@ namespace ngcore if(!funcname.empty() && !libname.empty()) { - std::array buffer; - int status; - size_t size = buffer.size(); - abi::__cxa_demangle(funcname.c_str(), buffer.data(), &size, &status); - out << "in " << yellow << buffer.data() << reset_shell; - std::string nm_command = "nm " + libname + " | grep \"" + funcname + "$\" | cut -f 1 -d ' '"; - std::output; + std::string output; auto exit_code = exec(nm_command, output); auto fptr = std::strtoul(output.c_str(), 0, 16); if(fptr == 0) @@ -138,6 +135,7 @@ namespace ngcore std::string GetBackTrace() { + std::cerr << "Collecting backtrace..." << std::endl; std::stringstream result; void *bt[1024]; int bt_size; @@ -159,6 +157,34 @@ namespace ngcore } // namespace ngcore +static void ngcore_signal_handler(int sig) +{ + switch(sig) + { + case SIGABRT: + std::cerr << "Caught SIGABRT: usually caused by abort() or assert()" << std::endl; + break; + case SIGILL: + std::cerr << "Caught SIGILL: illegal instruction" << std::endl; + break; + case SIGSEGV: + std::cerr << "Caught SIGSEGV: segmentation fault" << std::endl; + break; + } + + std::cerr << ngcore::GetBackTrace() << std::endl; + exit(1); +} + +// register signal handler when library is loaded +static bool dummy = []() +{ + signal(SIGABRT, ngcore_signal_handler); + signal(SIGILL, ngcore_signal_handler); + signal(SIGSEGV, ngcore_signal_handler); + return true; +}(); + #else // __GNUC__ namespace ngcore