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 was developed by the LLVM research group and is distributed under
00006 // the University of Illinois Open Source 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 <vector>
00017 #include <algorithm>
00018 #if HAVE_EXECINFO_H
00019 # include <execinfo.h>         // For backtrace().
00020 #endif
00021 #if HAVE_SIGNAL_H
00022 #include <signal.h>
00023 #endif
00024 
00025 namespace {
00026 
00027 bool StackTraceRequested = false; 
00028 
00029 /// InterruptFunction - The function to call if ctrl-c is pressed.
00030 void (*InterruptFunction)() = 0;
00031 
00032 std::vector<std::string> *FilesToRemove = 0 ;
00033 std::vector<llvm::sys::Path> *DirectoriesToRemove = 0;
00034 
00035 // IntSigs - Signals that may interrupt the program at any time.
00036 const int IntSigs[] = {
00037   SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2
00038 };
00039 const int *IntSigsEnd = IntSigs + sizeof(IntSigs)/sizeof(IntSigs[0]);
00040 
00041 // KillSigs - Signals that are synchronous with the program that will cause it
00042 // to die.
00043 const int KillSigs[] = {
00044   SIGILL, SIGTRAP, SIGABRT, SIGFPE, SIGBUS, SIGSEGV, SIGSYS, SIGXCPU, SIGXFSZ
00045 #ifdef SIGEMT
00046   , SIGEMT
00047 #endif
00048 };
00049 const int *KillSigsEnd = KillSigs + sizeof(KillSigs)/sizeof(KillSigs[0]);
00050 
00051 #ifdef HAVE_BACKTRACE
00052 void* StackTrace[256];
00053 #endif
00054 
00055 // PrintStackTrace - In the case of a program crash or fault, print out a stack
00056 // trace so that the user has an indication of why and where we died.
00057 //
00058 // On glibc systems we have the 'backtrace' function, which works nicely, but
00059 // doesn't demangle symbols.  In order to backtrace symbols, we fork and exec a
00060 // 'c++filt' process to do the demangling.  This seems like the simplest and
00061 // most robust solution when we can't allocate memory (such as in a signal
00062 // handler).  If we can't find 'c++filt', we fallback to printing mangled names.
00063 //
00064 void PrintStackTrace() {
00065 #ifdef HAVE_BACKTRACE
00066   // Use backtrace() to output a backtrace on Linux systems with glibc.
00067   int depth = backtrace(StackTrace, sizeof(StackTrace)/sizeof(StackTrace[0]));
00068   
00069   // Create a one-way unix pipe.  The backtracing process writes to PipeFDs[1],
00070   // the c++filt process reads from PipeFDs[0].
00071   int PipeFDs[2];
00072   if (pipe(PipeFDs)) {
00073     backtrace_symbols_fd(StackTrace, depth, STDERR_FILENO);
00074     return;
00075   }
00076 
00077   switch (pid_t ChildPID = fork()) {
00078   case -1:        // Error forking, print mangled stack trace
00079     close(PipeFDs[0]);
00080     close(PipeFDs[1]);
00081     backtrace_symbols_fd(StackTrace, depth, STDERR_FILENO);
00082     return;
00083   default:        // backtracing process
00084     close(PipeFDs[0]);  // Close the reader side.
00085 
00086     // Print the mangled backtrace into the pipe.
00087     backtrace_symbols_fd(StackTrace, depth, PipeFDs[1]);
00088     close(PipeFDs[1]);   // We are done writing.
00089     while (waitpid(ChildPID, 0, 0) == -1)
00090       if (errno != EINTR) break;
00091     return;
00092 
00093   case 0:         // c++filt process
00094     close(PipeFDs[1]);    // Close the writer side.
00095     dup2(PipeFDs[0], 0);  // Read from standard input
00096     close(PipeFDs[0]);    // Close the old descriptor
00097     dup2(2, 1);           // Revector stdout -> stderr
00098 
00099     // Try to run c++filt or gc++filt.  If neither is found, call back on 'cat'
00100     // to print the mangled stack trace.  If we can't find cat, just exit.
00101     execlp("c++filt", "c++filt", (char*)NULL);
00102     execlp("gc++filt", "gc++filt", (char*)NULL);
00103     execlp("cat", "cat", (char*)NULL);
00104     execlp("/bin/cat", "cat", (char*)NULL);
00105     exit(0);
00106   }
00107 #endif
00108 }
00109 
00110 // SignalHandler - The signal handler that runs...
00111 RETSIGTYPE SignalHandler(int Sig) {
00112   if (FilesToRemove != 0)
00113     while (!FilesToRemove->empty()) {
00114       std::remove(FilesToRemove->back().c_str());
00115       FilesToRemove->pop_back();
00116     }
00117 
00118   if (DirectoriesToRemove != 0)
00119     while (!DirectoriesToRemove->empty()) {
00120       DirectoriesToRemove->back().eraseFromDisk(true);
00121       DirectoriesToRemove->pop_back();
00122     }
00123 
00124   if (std::find(IntSigs, IntSigsEnd, Sig) != IntSigsEnd) {
00125     if (InterruptFunction) {
00126       void (*IF)() = InterruptFunction;
00127       InterruptFunction = 0;
00128       IF();        // run the interrupt function.
00129       return;
00130     } else {
00131       exit(1);   // If this is an interrupt signal, exit the program
00132     }
00133   }
00134 
00135   // Otherwise if it is a fault (like SEGV) output the stacktrace to
00136   // STDERR (if we can) and reissue the signal to die...
00137   if (StackTraceRequested)
00138     PrintStackTrace();
00139   signal(Sig, SIG_DFL);
00140 }
00141 
00142 // Just call signal
00143 void RegisterHandler(int Signal) { 
00144   signal(Signal, SignalHandler); 
00145 }
00146 
00147 }
00148 
00149 namespace llvm {
00150 
00151 void sys::SetInterruptFunction(void (*IF)()) {
00152   InterruptFunction = IF;
00153   RegisterHandler(SIGINT);
00154 }
00155 
00156 // RemoveFileOnSignal - The public API
00157 void sys::RemoveFileOnSignal(const sys::Path &Filename) {
00158   if (FilesToRemove == 0)
00159     FilesToRemove = new std::vector<std::string>;
00160 
00161   FilesToRemove->push_back(Filename.toString());
00162 
00163   std::for_each(IntSigs, IntSigsEnd, RegisterHandler);
00164   std::for_each(KillSigs, KillSigsEnd, RegisterHandler);
00165 }
00166 
00167 // RemoveDirectoryOnSignal - The public API
00168 void sys::RemoveDirectoryOnSignal(const llvm::sys::Path& path) {
00169   if (!path.isDirectory())
00170     return;
00171 
00172   if (DirectoriesToRemove == 0)
00173     DirectoriesToRemove = new std::vector<sys::Path>;
00174 
00175   DirectoriesToRemove->push_back(path);
00176 
00177   std::for_each(IntSigs, IntSigsEnd, RegisterHandler);
00178   std::for_each(KillSigs, KillSigsEnd, RegisterHandler);
00179 }
00180 
00181 /// PrintStackTraceOnErrorSignal - When an error signal (such as SIBABRT or
00182 /// SIGSEGV) is delivered to the process, print a stack trace and then exit.
00183 void sys::PrintStackTraceOnErrorSignal() {
00184   StackTraceRequested = true;
00185   std::for_each(KillSigs, KillSigsEnd, RegisterHandler);
00186 }
00187 
00188 }
00189