From b9632bee7ca842c2721340c42e18bfd0109ce5aa Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 21 Feb 2004 21:06:19 +0000 Subject: [PATCH] When printing a stack trace, demangle it if possible. Since we are potentially in a signal handler, allocating memory or doing other unsafe things is bad, which means we should do it in a different process. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@11689 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Support/Signals.cpp | 66 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/lib/Support/Signals.cpp b/lib/Support/Signals.cpp index 978d3b7ccb2..d4e74aa089d 100644 --- a/lib/Support/Signals.cpp +++ b/lib/Support/Signals.cpp @@ -23,6 +23,8 @@ #endif #include #include +#include +#include using namespace llvm; static std::vector FilesToRemove; @@ -43,7 +45,65 @@ static const int KillSigs[] = { }; static const int *KillSigsEnd = KillSigs + sizeof(KillSigs)/sizeof(KillSigs[0]); +#ifdef HAVE_BACKTRACE static void* StackTrace[256]; +#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. In order to backtrace symbols, we fork and exec a +// 'c++filt' process to do the demangling. This seems like the simplest and +// most robust solution when we can't allocate memory (such as in a signal +// handler). If we can't find 'c++filt', we fallback to printing mangled names. +// +static void PrintStackTrace() { +#ifdef HAVE_BACKTRACE + // Use backtrace() to output a backtrace on Linux systems with glibc. + int depth = backtrace(StackTrace, sizeof(StackTrace)/sizeof(StackTrace[0])); + + // Create a one-way unix pipe. The backtracing process writes to PipeFDs[1], + // the c++filt process reads from PipeFDs[0]. + int PipeFDs[2]; + if (pipe(PipeFDs)) { + backtrace_symbols_fd(StackTrace, depth, STDERR_FILENO); + return; + } + + switch (pid_t ChildPID = fork()) { + case -1: // Error forking, print mangled stack trace + close(PipeFDs[0]); + close(PipeFDs[1]); + backtrace_symbols_fd(StackTrace, depth, STDERR_FILENO); + return; + default: // backtracing process + close(PipeFDs[0]); // Close the reader side. + + // Print the mangled backtrace into the pipe. + backtrace_symbols_fd(StackTrace, depth, PipeFDs[1]); + close(PipeFDs[1]); // We are done writing. + while (waitpid(ChildPID, 0, 0) == -1) + if (errno != EINTR) break; + return; + + case 0: // c++filt process + close(PipeFDs[1]); // Close the writer side. + dup2(PipeFDs[0], 0); // Read from standard input + close(PipeFDs[0]); // Close the old descriptor + dup2(2, 1); // Revector stdout -> stderr + + // Try to run c++filt or gc++filt. If neither is found, call back on 'cat' + // to print the mangled stack trace. If we can't find cat, just exit. + execlp("c++filt", "c++filt", 0); + execlp("gc++filt", "gc++filt", 0); + execlp("cat", "cat", 0); + execlp("/usr/bin/cat", "cat", 0); + exit(0); + } +#endif +} // SignalHandler - The signal handler that runs... static RETSIGTYPE SignalHandler(int Sig) { @@ -57,11 +117,7 @@ static RETSIGTYPE SignalHandler(int Sig) { // Otherwise if it is a fault (like SEGV) output the stacktrace to // STDERR (if we can) and reissue the signal to die... -#ifdef HAVE_BACKTRACE - // Use backtrace() to output a backtrace on Linux systems with glibc. - int depth = backtrace(StackTrace, sizeof(StackTrace)/sizeof(StackTrace[0])); - backtrace_symbols_fd(StackTrace, depth, STDERR_FILENO); -#endif + PrintStackTrace(); signal(Sig, SIG_DFL); }