CrashRecovery/Darwin: On Darwin, raise sends a signal to the main thread instead
[oota-llvm.git] / lib / System / Unix / Signals.inc
index 35c628ba33f509dd0d780a59c3508993a436b035..3e0de66b5d71e0f2f104bf8fbe91c81f501f9e6a 100644 (file)
@@ -2,8 +2,8 @@
 // 
 //                     The LLVM Compiler Infrastructure
 //
-// This file was developed by the LLVM research group and is distributed under
-// the University of Illinois Open Source License. See LICENSE.TXT for details.
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
 // 
 //===----------------------------------------------------------------------===//
 //
@@ -13,6 +13,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "Unix.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/System/Mutex.h"
 #include <vector>
 #include <algorithm>
 #if HAVE_EXECINFO_H
 #if HAVE_SIGNAL_H
 #include <signal.h>
 #endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_DLFCN_H && __GNUG__
+#include <dlfcn.h>
+#include <cxxabi.h> 
+#endif
 using namespace llvm;
 
-namespace {
+static RETSIGTYPE SignalHandler(int Sig);  // defined below.
 
-bool StackTraceRequested = false; 
+static SmartMutex<true> SignalsMutex;
 
 /// InterruptFunction - The function to call if ctrl-c is pressed.
-void (*InterruptFunction)() = 0;
+static void (*InterruptFunction)() = 0;
 
-std::vector<sys::Path> *FilesToRemove = 0 ;
-std::vector<sys::Path> *DirectoriesToRemove = 0;
+static std::vector<sys::Path> FilesToRemove;
+static std::vector<std::pair<void(*)(void*), void*> > CallBacksToRun;
 
 // IntSigs - Signals that may interrupt the program at any time.
-const int IntSigs[] = {
+static const int IntSigs[] = {
   SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2
 };
-const int *IntSigsEnd = IntSigs + sizeof(IntSigs)/sizeof(IntSigs[0]);
+static const int *const IntSigsEnd =
+  IntSigs + sizeof(IntSigs) / sizeof(IntSigs[0]);
 
 // KillSigs - Signals that are synchronous with the program that will cause it
 // to die.
-const int KillSigs[] = {
-  SIGILL, SIGTRAP, SIGABRT, SIGFPE, SIGBUS, SIGSEGV, SIGSYS, SIGXCPU, SIGXFSZ
+static const int KillSigs[] = {
+  SIGILL, SIGTRAP, SIGABRT, SIGFPE, SIGBUS, SIGSEGV
+#ifdef SIGSYS
+  , SIGSYS
+#endif
+#ifdef SIGXCPU
+  , SIGXCPU
+#endif
+#ifdef SIGXFSZ
+  , SIGXFSZ
+#endif
 #ifdef SIGEMT
   , SIGEMT
 #endif
 };
-const int *KillSigsEnd = KillSigs + sizeof(KillSigs)/sizeof(KillSigs[0]);
+static const int *const KillSigsEnd =
+  KillSigs + sizeof(KillSigs) / sizeof(KillSigs[0]);
 
-#ifdef HAVE_BACKTRACE
-void* StackTrace[256];
-#endif
+static unsigned NumRegisteredSignals = 0;
+static struct {
+  struct sigaction SA;
+  int SigNo;
+} RegisteredSignalInfo[(sizeof(IntSigs)+sizeof(KillSigs))/sizeof(KillSigs[0])];
 
-// 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.
-//
-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]));
+
+static void RegisterHandler(int Signal) {
+  assert(NumRegisteredSignals <
+         sizeof(RegisteredSignalInfo)/sizeof(RegisteredSignalInfo[0]) &&
+         "Out of space for signal handlers!");
+
+  struct sigaction NewHandler;
   
-  // 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;
-  }
+  NewHandler.sa_handler = SignalHandler;
+  NewHandler.sa_flags = SA_NODEFER|SA_RESETHAND;
+  sigemptyset(&NewHandler.sa_mask); 
+  
+  // Install the new handler, save the old one in RegisteredSignalInfo.
+  sigaction(Signal, &NewHandler,
+            &RegisteredSignalInfo[NumRegisteredSignals].SA);
+  RegisteredSignalInfo[NumRegisteredSignals].SigNo = Signal;
+  ++NumRegisteredSignals;
+}
+
+static void RegisterHandlers() {
+  // If the handlers are already registered, we're done.
+  if (NumRegisteredSignals != 0) return;
+
+  std::for_each(IntSigs, IntSigsEnd, RegisterHandler);
+  std::for_each(KillSigs, KillSigsEnd, RegisterHandler);
+}
+
+static void UnregisterHandlers() {
+  // Restore all of the signal handlers to how they were before we showed up.
+  for (unsigned i = 0, e = NumRegisteredSignals; i != e; ++i)
+    sigaction(RegisteredSignalInfo[i].SigNo,
+              &RegisteredSignalInfo[i].SA, 0);
+  NumRegisteredSignals = 0;
+}
 
-  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", (char*)NULL);
-    execlp("gc++filt", "gc++filt", (char*)NULL);
-    execlp("cat", "cat", (char*)NULL);
-    execlp("/bin/cat", "cat", (char*)NULL);
-    exit(0);
+/// RemoveFilesToRemove - Process the FilesToRemove list. This function
+/// should be called with the SignalsMutex lock held.
+static void RemoveFilesToRemove() {
+  while (!FilesToRemove.empty()) {
+    FilesToRemove.back().eraseFromDisk(true);
+    FilesToRemove.pop_back();
   }
-#endif
 }
 
-// SignalHandler - The signal handler that runs...
-RETSIGTYPE SignalHandler(int Sig) {
-  if (FilesToRemove != 0)
-    while (!FilesToRemove->empty()) {
-      FilesToRemove->back().eraseFromDisk(true);
-      FilesToRemove->pop_back();
-    }
+// SignalHandler - The signal handler that runs.
+static RETSIGTYPE SignalHandler(int Sig) {
+  // Restore the signal behavior to default, so that the program actually
+  // crashes when we return and the signal reissues.  This also ensures that if
+  // we crash in our signal handler that the program will terminate immediately
+  // instead of recursing in the signal handler.
+  UnregisterHandlers();
 
-  if (DirectoriesToRemove != 0)
-    while (!DirectoriesToRemove->empty()) {
-      DirectoriesToRemove->back().eraseFromDisk(true);
-      DirectoriesToRemove->pop_back();
-    }
+  // Unmask all potentially blocked kill signals.
+  sigset_t SigMask;
+  sigfillset(&SigMask);
+  sigprocmask(SIG_UNBLOCK, &SigMask, 0);
+
+  SignalsMutex.acquire();
+  RemoveFilesToRemove();
 
   if (std::find(IntSigs, IntSigsEnd, Sig) != IntSigsEnd) {
     if (InterruptFunction) {
       void (*IF)() = InterruptFunction;
+      SignalsMutex.release();
       InterruptFunction = 0;
       IF();        // run the interrupt function.
       return;
-    } else {
-      exit(1);   // If this is an interrupt signal, exit the program
     }
+    
+    SignalsMutex.release();
+    raise(Sig);   // Execute the default handler.
+    return;
   }
 
-  // Otherwise if it is a fault (like SEGV) output the stacktrace to
-  // STDERR (if we can) and reissue the signal to die...
-  if (StackTraceRequested)
-    PrintStackTrace();
-  signal(Sig, SIG_DFL);
-}
+  SignalsMutex.release();
 
-// Just call signal
-void RegisterHandler(int Signal) { 
-  signal(Signal, SignalHandler); 
+  // Otherwise if it is a fault (like SEGV) run any handler.
+  for (unsigned i = 0, e = CallBacksToRun.size(); i != e; ++i)
+    CallBacksToRun[i].first(CallBacksToRun[i].second);
 }
 
+void llvm::sys::RunInterruptHandlers() {
+  SignalsMutex.acquire();
+  RemoveFilesToRemove();
+  SignalsMutex.release();
 }
 
-
-void sys::SetInterruptFunction(void (*IF)()) {
+void llvm::sys::SetInterruptFunction(void (*IF)()) {
+  SignalsMutex.acquire();
   InterruptFunction = IF;
-  RegisterHandler(SIGINT);
+  SignalsMutex.release();
+  RegisterHandlers();
 }
 
 // RemoveFileOnSignal - The public API
-bool sys::RemoveFileOnSignal(const sys::Path &Filename, std::string* ErrMsg) {
-  if (FilesToRemove == 0)
-    FilesToRemove = new std::vector<sys::Path>;
+bool llvm::sys::RemoveFileOnSignal(const sys::Path &Filename,
+                                   std::string* ErrMsg) {
+  SignalsMutex.acquire();
+  FilesToRemove.push_back(Filename);
 
-  FilesToRemove->push_back(Filename);
+  SignalsMutex.release();
 
-  std::for_each(IntSigs, IntSigsEnd, RegisterHandler);
-  std::for_each(KillSigs, KillSigsEnd, RegisterHandler);
+  RegisterHandlers();
   return false;
 }
 
-// RemoveDirectoryOnSignal - The public API
-bool sys::RemoveDirectoryOnSignal(const sys::Path& path, std::string* ErrMsg) {
-  // Not a directory?
-  sys::FileStatus Status;
-  if (path.getFileStatus(Status) || !Status.isDir) {
-    if (ErrMsg)
-      *ErrMsg = path.toString() + " is not a directory";
-    return true;
+/// AddSignalHandler - Add a function to be called when a signal is delivered
+/// to the process.  The handler can have a cookie passed to it to identify
+/// what instance of the handler it is.
+void llvm::sys::AddSignalHandler(void (*FnPtr)(void *), void *Cookie) {
+  CallBacksToRun.push_back(std::make_pair(FnPtr, Cookie));
+  RegisterHandlers();
+}
+
+
+// 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.  
+static void PrintStackTrace(void *) {
+#ifdef HAVE_BACKTRACE
+  static void* StackTrace[256];
+  // Use backtrace() to output a backtrace on Linux systems with glibc.
+  int depth = backtrace(StackTrace,
+                        static_cast<int>(array_lengthof(StackTrace)));
+#if HAVE_DLFCN_H && __GNUG__
+  int width = 0;
+  for (int i = 0; i < depth; ++i) {
+    Dl_info dlinfo;
+    dladdr(StackTrace[i], &dlinfo);
+    const char* name = strrchr(dlinfo.dli_fname, '/');
+
+    int nwidth;
+    if (name == NULL) nwidth = strlen(dlinfo.dli_fname);
+    else              nwidth = strlen(name) - 1;
+
+    if (nwidth > width) width = nwidth;
   }
 
-  if (DirectoriesToRemove == 0)
-    DirectoriesToRemove = new std::vector<sys::Path>;
+  for (int i = 0; i < depth; ++i) {
+    Dl_info dlinfo;
+    dladdr(StackTrace[i], &dlinfo);
 
-  DirectoriesToRemove->push_back(path);
+    fprintf(stderr, "%-2d", i);
 
-  std::for_each(IntSigs, IntSigsEnd, RegisterHandler);
-  std::for_each(KillSigs, KillSigsEnd, RegisterHandler);
-  return false;
+    const char* name = strrchr(dlinfo.dli_fname, '/');
+    if (name == NULL) fprintf(stderr, " %-*s", width, dlinfo.dli_fname);
+    else              fprintf(stderr, " %-*s", width, name+1);
+
+    fprintf(stderr, " %#0*lx",
+            (int)(sizeof(void*) * 2) + 2, (unsigned long)StackTrace[i]);
+
+    if (dlinfo.dli_sname != NULL) {
+      int res;
+      fputc(' ', stderr);
+      char* d = abi::__cxa_demangle(dlinfo.dli_sname, NULL, NULL, &res);
+      if (d == NULL) fputs(dlinfo.dli_sname, stderr);
+      else           fputs(d, stderr);
+      free(d);
+
+      fprintf(stderr, " + %tu",(char*)StackTrace[i]-(char*)dlinfo.dli_saddr);
+    }
+    fputc('\n', stderr);
+  }
+#else
+  backtrace_symbols_fd(StackTrace, depth, STDERR_FILENO);
+#endif
+#endif
 }
 
 /// PrintStackTraceOnErrorSignal - When an error signal (such as SIBABRT or
 /// SIGSEGV) is delivered to the process, print a stack trace and then exit.
-void sys::PrintStackTraceOnErrorSignal() {
-  StackTraceRequested = true;
-  std::for_each(KillSigs, KillSigsEnd, RegisterHandler);
+void llvm::sys::PrintStackTraceOnErrorSignal() {
+  AddSignalHandler(PrintStackTrace, 0);
+}
+
+
+/***/
+
+// On Darwin, raise sends a signal to the main thread instead of the current
+// thread. This has the unfortunate effect that assert() and abort() will end up
+// bypassing our crash recovery attempts. We work around this for anything in
+// the same linkage unit by just defining our own versions of the assert handler
+// and abort.
+
+#ifdef __APPLE__
+
+void __assert_rtn(const char *func,
+                  const char *file,
+                  int line,
+                  const char *expr) {
+  if (func)
+    fprintf(stderr, "Assertion failed: (%s), function %s, file %s, line %d.\n",
+            expr, func, file, line);
+  else
+    fprintf(stderr, "Assertion failed: (%s), file %s, line %d.\n",
+            expr, file, line);
+  abort();
 }
+
+#include <signal.h>
+#include <pthread.h>
+
+void abort() {
+  pthread_kill(pthread_self(), SIGABRT);
+  usleep(1000);
+  __builtin_trap();
+}
+
+#endif