From b9f1b721c16970687690a0b4146d0c7346d355c2 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 18 May 2016 22:26:36 +0000 Subject: [PATCH] Work around a glibc bug: backtrace() spuriously fails if - glibc is dynamically linked, and - libgcc_s is unavailable (for instance, another library is being used to provide the compiler runtime or libgcc is statically linked), and - the target is x86_64. If we run backtrace() and it fails to find any stack frames, try using _Unwind_Backtrace instead if available. llvm-svn: 269992 --- cmake/config-ix.cmake | 1 + lib/Support/Unix/Signals.inc | 55 +++++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake index 7d5277517a9..b3f225f0740 100755 --- a/cmake/config-ix.cmake +++ b/cmake/config-ix.cmake @@ -144,6 +144,7 @@ endif() # function checks check_symbol_exists(arc4random "stdlib.h" HAVE_DECL_ARC4RANDOM) check_symbol_exists(backtrace "execinfo.h" HAVE_BACKTRACE) +check_symbol_exists(_Unwind_Backtrace "unwind.h" HAVE_UNWIND_BACKTRACE) check_symbol_exists(getpagesize unistd.h HAVE_GETPAGESIZE) check_symbol_exists(getrusage sys/resource.h HAVE_GETRUSAGE) check_symbol_exists(setrlimit sys/resource.h HAVE_SETRLIMIT) diff --git a/lib/Support/Unix/Signals.inc b/lib/Support/Unix/Signals.inc index 061cdb3da21..27f3414ce57 100644 --- a/lib/Support/Unix/Signals.inc +++ b/lib/Support/Unix/Signals.inc @@ -45,6 +45,9 @@ #if HAVE_LINK_H #include #endif +#if HAVE_UNWIND_BACKTRACE +#include +#endif using namespace llvm; @@ -309,17 +312,61 @@ static bool findModulesAndOffsets(void **StackTrace, int Depth, } #endif // defined(HAVE_BACKTRACE) && defined(ENABLE_BACKTRACES) && ... +#if defined(ENABLE_BACKTRACES) && defined(HAVE_UNWIND_BACKTRACE) +static int unwindBacktrace(void **StackTrace, int MaxEntries) { + if (MaxEntries < 0) + return 0; + + // Skip the first frame ('unwindBacktrace' itself). + int Entries = -1; + + auto HandleFrame = [&](_Unwind_Context *Context) -> _Unwind_Reason_Code { + // Apparently we need to detect reaching the end of the stack ourselves. + void *IP = (void *)_Unwind_GetIP(Context); + if (!IP) + return _URC_END_OF_STACK; + + assert(Entries < MaxEntries && "recursively called after END_OF_STACK?"); + if (Entries >= 0) + StackTrace[Entries] = IP; + + if (++Entries == MaxEntries) + return _URC_END_OF_STACK; + return _URC_NO_REASON; + }; + + _Unwind_Backtrace( + [](_Unwind_Context *Context, void *Handler) { + return (*static_cast(Handler))(Context); + }, + static_cast(&HandleFrame)); + return std::max(Entries, 0); +} +#endif + // PrintStackTrace - In the case of a program crash or fault, print out a stack // trace so that the user has an indication of why and where we died. // // On glibc systems we have the 'backtrace' function, which works nicely, but // doesn't demangle symbols. void llvm::sys::PrintStackTrace(raw_ostream &OS) { -#if defined(HAVE_BACKTRACE) && defined(ENABLE_BACKTRACES) - static void* StackTrace[256]; +#if defined(ENABLE_BACKTRACES) + static void *StackTrace[256]; + int depth = 0; +#if defined(HAVE_BACKTRACE) // Use backtrace() to output a backtrace on Linux systems with glibc. - int depth = backtrace(StackTrace, + if (!depth) + depth = backtrace(StackTrace, static_cast(array_lengthof(StackTrace))); +#endif +#if defined(HAVE_UNWIND_BACKTRACE) + // Try _Unwind_Backtrace() if backtrace() failed. + if (!depth) + depth = unwindBacktrace(StackTrace, static_cast(array_lengthof(StackTrace))); +#endif + if (!depth) + return; + if (printSymbolizedStackTrace(StackTrace, depth, OS)) return; #if HAVE_DLFCN_H && __GNUG__ @@ -369,7 +416,7 @@ void llvm::sys::PrintStackTrace(raw_ostream &OS) { } OS << '\n'; } -#else +#elif defined(HAVE_BACKTRACE) backtrace_symbols_fd(StackTrace, depth, STDERR_FILENO); #endif #endif