LLVM API Documentation

Unix/Signals.inc

Go to the documentation of this file.
00001 //===- Signals.cpp - Generic Unix Signals Implementation -----*- C++ -*-===//
00002 // 
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 // 
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // This file defines some helpful functions for dealing with the possibility of
00011 // Unix signals occuring while your program is running.
00012 //
00013 //===----------------------------------------------------------------------===//
00014 
00015 #include "Unix.h"
00016 #include "llvm/ADT/STLExtras.h"
00017 #include <vector>
00018 #include <algorithm>
00019 #if HAVE_EXECINFO_H
00020 # include <execinfo.h>         // For backtrace().
00021 #endif
00022 #if HAVE_SIGNAL_H
00023 #include <signal.h>
00024 #endif
00025 #if HAVE_SYS_STAT_H
00026 #include <sys/stat.h>
00027 #endif
00028 #if HAVE_DLFCN_H && __GNUG__
00029 #include <dlfcn.h>
00030 #include <cxxabi.h> 
00031 #endif
00032 using namespace llvm;
00033 
00034 namespace {
00035 
00036 static bool StackTraceRequested = false; 
00037 
00038 /// InterruptFunction - The function to call if ctrl-c is pressed.
00039 static void (*InterruptFunction)() = 0;
00040 
00041 static std::vector<sys::Path> *FilesToRemove = 0 ;
00042 static std::vector<sys::Path> *DirectoriesToRemove = 0;
00043 
00044 // IntSigs - Signals that may interrupt the program at any time.
00045 static const int IntSigs[] = {
00046   SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2
00047 };
00048 static const int *const IntSigsEnd =
00049   IntSigs + sizeof(IntSigs) / sizeof(IntSigs[0]);
00050 
00051 // KillSigs - Signals that are synchronous with the program that will cause it
00052 // to die.
00053 static const int KillSigs[] = {
00054   SIGILL, SIGTRAP, SIGABRT, SIGFPE, SIGBUS, SIGSEGV, SIGSYS, SIGXCPU, SIGXFSZ
00055 #ifdef SIGEMT
00056   , SIGEMT
00057 #endif
00058 };
00059 static const int *const KillSigsEnd =
00060   KillSigs + sizeof(KillSigs) / sizeof(KillSigs[0]);
00061 
00062 #ifdef HAVE_BACKTRACE
00063 static void* StackTrace[256];
00064 #endif
00065 
00066 // PrintStackTrace - In the case of a program crash or fault, print out a stack
00067 // trace so that the user has an indication of why and where we died.
00068 //
00069 // On glibc systems we have the 'backtrace' function, which works nicely, but
00070 // doesn't demangle symbols.  
00071 static void PrintStackTrace() {
00072 #ifdef HAVE_BACKTRACE
00073   // Use backtrace() to output a backtrace on Linux systems with glibc.
00074   int depth = backtrace(StackTrace,
00075                         static_cast<int>(array_lengthof(StackTrace)));
00076 #if HAVE_DLFCN_H && __GNUG__
00077   int width = 0;
00078   for (int i = 0; i < depth; ++i) {
00079     Dl_info dlinfo;
00080     dladdr(StackTrace[i], &dlinfo);
00081     const char* name = strrchr(dlinfo.dli_fname, '/');
00082 
00083     int nwidth;
00084     if (name == NULL) nwidth = strlen(dlinfo.dli_fname);
00085     else              nwidth = strlen(name) - 1;
00086 
00087     if (nwidth > width) width = nwidth;
00088   }
00089 
00090   for (int i = 0; i < depth; ++i) {
00091     Dl_info dlinfo;
00092     dladdr(StackTrace[i], &dlinfo);
00093 
00094     fprintf(stderr, "%-3d", i);
00095 
00096     const char* name = strrchr(dlinfo.dli_fname, '/');
00097     if (name == NULL) fprintf(stderr, " %-*s", width, dlinfo.dli_fname);
00098     else              fprintf(stderr, " %-*s", width, name+1);
00099 
00100     fprintf(stderr, " %#0*lx",
00101             (int)(sizeof(void*) * 2) + 2, (unsigned long)StackTrace[i]);
00102 
00103     if (dlinfo.dli_sname != NULL) {
00104       int res;
00105       fputc(' ', stderr);
00106       char* d = abi::__cxa_demangle(dlinfo.dli_sname, NULL, NULL, &res);
00107       if (d == NULL) fputs(dlinfo.dli_sname, stderr);
00108       else           fputs(d, stderr);
00109       free(d);
00110 
00111       fprintf(stderr, " + %tu",(char*)StackTrace[i]-(char*)dlinfo.dli_saddr);
00112     }
00113     fputc('\n', stderr);
00114   }
00115 #else
00116   backtrace_symbols_fd(StackTrace, depth, STDERR_FILENO);
00117 #endif
00118 #endif
00119 }
00120 
00121 // SignalHandler - The signal handler that runs...
00122 static RETSIGTYPE SignalHandler(int Sig) {
00123   if (FilesToRemove != 0)
00124     while (!FilesToRemove->empty()) {
00125       FilesToRemove->back().eraseFromDisk(true);
00126       FilesToRemove->pop_back();
00127     }
00128 
00129   if (DirectoriesToRemove != 0)
00130     while (!DirectoriesToRemove->empty()) {
00131       DirectoriesToRemove->back().eraseFromDisk(true);
00132       DirectoriesToRemove->pop_back();
00133     }
00134 
00135   if (std::find(IntSigs, IntSigsEnd, Sig) != IntSigsEnd) {
00136     if (InterruptFunction) {
00137       void (*IF)() = InterruptFunction;
00138       InterruptFunction = 0;
00139       IF();        // run the interrupt function.
00140       return;
00141     } else {
00142       exit(1);   // If this is an interrupt signal, exit the program
00143     }
00144   }
00145 
00146   // Otherwise if it is a fault (like SEGV) output the stacktrace to
00147   // STDERR (if we can) and reissue the signal to die...
00148   if (StackTraceRequested)
00149     PrintStackTrace();
00150   signal(Sig, SIG_DFL);
00151 }
00152 
00153 // Just call signal
00154 static void RegisterHandler(int Signal) { 
00155   signal(Signal, SignalHandler); 
00156 }
00157 
00158 }
00159 
00160 
00161 void sys::SetInterruptFunction(void (*IF)()) {
00162   InterruptFunction = IF;
00163   RegisterHandler(SIGINT);
00164 }
00165 
00166 // RemoveFileOnSignal - The public API
00167 bool sys::RemoveFileOnSignal(const sys::Path &Filename, std::string* ErrMsg) {
00168   if (FilesToRemove == 0)
00169     FilesToRemove = new std::vector<sys::Path>;
00170 
00171   FilesToRemove->push_back(Filename);
00172 
00173   std::for_each(IntSigs, IntSigsEnd, RegisterHandler);
00174   std::for_each(KillSigs, KillSigsEnd, RegisterHandler);
00175   return false;
00176 }
00177 
00178 // RemoveDirectoryOnSignal - The public API
00179 bool sys::RemoveDirectoryOnSignal(const sys::Path& path, std::string* ErrMsg) {
00180   // Not a directory?
00181   struct stat buf;
00182   if (0 != stat(path.c_str(), &buf)) {
00183     MakeErrMsg(ErrMsg, path.toString() + ": can't get status of file");
00184     return true;
00185   }
00186 
00187   if (!S_ISDIR(buf.st_mode)) {
00188     if (ErrMsg)
00189       *ErrMsg = path.toString() + " is not a directory";
00190     return true;
00191   }
00192 
00193   if (DirectoriesToRemove == 0)
00194     DirectoriesToRemove = new std::vector<sys::Path>;
00195 
00196   DirectoriesToRemove->push_back(path);
00197 
00198   std::for_each(IntSigs, IntSigsEnd, RegisterHandler);
00199   std::for_each(KillSigs, KillSigsEnd, RegisterHandler);
00200   return false;
00201 }
00202 
00203 /// PrintStackTraceOnErrorSignal - When an error signal (such as SIBABRT or
00204 /// SIGSEGV) is delivered to the process, print a stack trace and then exit.
00205 void sys::PrintStackTraceOnErrorSignal() {
00206   StackTraceRequested = true;
00207   std::for_each(KillSigs, KillSigsEnd, RegisterHandler);
00208 }