| Reid Spencer | 3d7a614 | 2004-08-29 19:22:48 +0000 | [diff] [blame] | 1 | //===- Signals.cpp - Generic Unix Signals Implementation -----*- C++ -*-===// | 
|  | 2 | // | 
|  | 3 | //                     The LLVM Compiler Infrastructure | 
|  | 4 | // | 
|  | 5 | // This file was developed by the LLVM research group and is distributed under | 
|  | 6 | // the University of Illinois Open Source License. See LICENSE.TXT for details. | 
|  | 7 | // | 
|  | 8 | //===----------------------------------------------------------------------===// | 
|  | 9 | // | 
|  | 10 | // This file defines some helpful functions for dealing with the possibility of | 
|  | 11 | // Unix signals occuring while your program is running. | 
|  | 12 | // | 
|  | 13 | //===----------------------------------------------------------------------===// | 
|  | 14 |  | 
|  | 15 | #include "Unix.h" | 
|  | 16 | #include <vector> | 
|  | 17 | #include <algorithm> | 
|  | 18 | #ifdef HAVE_EXECINFO_H | 
|  | 19 | # include <execinfo.h>         // For backtrace(). | 
|  | 20 | #endif | 
|  | 21 | #include <sys/wait.h> | 
|  | 22 | #include <signal.h> | 
|  | 23 |  | 
|  | 24 | namespace { | 
|  | 25 |  | 
|  | 26 | std::vector<std::string> *FilesToRemove = 0 ; | 
|  | 27 | std::vector<llvm::sys::Path> *DirectoriesToRemove = 0; | 
|  | 28 |  | 
|  | 29 | // IntSigs - Signals that may interrupt the program at any time. | 
|  | 30 | const int IntSigs[] = { | 
|  | 31 | SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2 | 
|  | 32 | }; | 
|  | 33 | const int *IntSigsEnd = IntSigs + sizeof(IntSigs)/sizeof(IntSigs[0]); | 
|  | 34 |  | 
|  | 35 | // KillSigs - Signals that are synchronous with the program that will cause it | 
|  | 36 | // to die. | 
|  | 37 | const int KillSigs[] = { | 
|  | 38 | SIGILL, SIGTRAP, SIGABRT, SIGFPE, SIGBUS, SIGSEGV, SIGSYS, SIGXCPU, SIGXFSZ | 
|  | 39 | #ifdef SIGEMT | 
|  | 40 | , SIGEMT | 
|  | 41 | #endif | 
|  | 42 | }; | 
|  | 43 | const int *KillSigsEnd = KillSigs + sizeof(KillSigs)/sizeof(KillSigs[0]); | 
|  | 44 |  | 
|  | 45 | #ifdef HAVE_BACKTRACE | 
|  | 46 | void* StackTrace[256]; | 
|  | 47 | #endif | 
|  | 48 |  | 
|  | 49 | // PrintStackTrace - In the case of a program crash or fault, print out a stack | 
|  | 50 | // trace so that the user has an indication of why and where we died. | 
|  | 51 | // | 
|  | 52 | // On glibc systems we have the 'backtrace' function, which works nicely, but | 
|  | 53 | // doesn't demangle symbols.  In order to backtrace symbols, we fork and exec a | 
|  | 54 | // 'c++filt' process to do the demangling.  This seems like the simplest and | 
|  | 55 | // most robust solution when we can't allocate memory (such as in a signal | 
|  | 56 | // handler).  If we can't find 'c++filt', we fallback to printing mangled names. | 
|  | 57 | // | 
|  | 58 | void PrintStackTrace() { | 
|  | 59 | #ifdef HAVE_BACKTRACE | 
|  | 60 | // Use backtrace() to output a backtrace on Linux systems with glibc. | 
|  | 61 | int depth = backtrace(StackTrace, sizeof(StackTrace)/sizeof(StackTrace[0])); | 
|  | 62 |  | 
|  | 63 | // Create a one-way unix pipe.  The backtracing process writes to PipeFDs[1], | 
|  | 64 | // the c++filt process reads from PipeFDs[0]. | 
|  | 65 | int PipeFDs[2]; | 
|  | 66 | if (pipe(PipeFDs)) { | 
|  | 67 | backtrace_symbols_fd(StackTrace, depth, STDERR_FILENO); | 
|  | 68 | return; | 
|  | 69 | } | 
|  | 70 |  | 
|  | 71 | switch (pid_t ChildPID = fork()) { | 
|  | 72 | case -1:        // Error forking, print mangled stack trace | 
|  | 73 | close(PipeFDs[0]); | 
|  | 74 | close(PipeFDs[1]); | 
|  | 75 | backtrace_symbols_fd(StackTrace, depth, STDERR_FILENO); | 
|  | 76 | return; | 
|  | 77 | default:        // backtracing process | 
|  | 78 | close(PipeFDs[0]);  // Close the reader side. | 
|  | 79 |  | 
|  | 80 | // Print the mangled backtrace into the pipe. | 
|  | 81 | backtrace_symbols_fd(StackTrace, depth, PipeFDs[1]); | 
|  | 82 | close(PipeFDs[1]);   // We are done writing. | 
|  | 83 | while (waitpid(ChildPID, 0, 0) == -1) | 
|  | 84 | if (errno != EINTR) break; | 
|  | 85 | return; | 
|  | 86 |  | 
|  | 87 | case 0:         // c++filt process | 
|  | 88 | close(PipeFDs[1]);    // Close the writer side. | 
|  | 89 | dup2(PipeFDs[0], 0);  // Read from standard input | 
|  | 90 | close(PipeFDs[0]);    // Close the old descriptor | 
|  | 91 | dup2(2, 1);           // Revector stdout -> stderr | 
|  | 92 |  | 
|  | 93 | // Try to run c++filt or gc++filt.  If neither is found, call back on 'cat' | 
|  | 94 | // to print the mangled stack trace.  If we can't find cat, just exit. | 
|  | 95 | execlp("c++filt", "c++filt", 0); | 
|  | 96 | execlp("gc++filt", "gc++filt", 0); | 
|  | 97 | execlp("cat", "cat", 0); | 
|  | 98 | execlp("/bin/cat", "cat", 0); | 
|  | 99 | exit(0); | 
|  | 100 | } | 
|  | 101 | #endif | 
|  | 102 | } | 
|  | 103 |  | 
|  | 104 | // SignalHandler - The signal handler that runs... | 
|  | 105 | RETSIGTYPE SignalHandler(int Sig) { | 
|  | 106 | if (FilesToRemove != 0) | 
|  | 107 | while (!FilesToRemove->empty()) { | 
|  | 108 | std::remove(FilesToRemove->back().c_str()); | 
|  | 109 | FilesToRemove->pop_back(); | 
|  | 110 | } | 
|  | 111 |  | 
|  | 112 | if (DirectoriesToRemove != 0) | 
|  | 113 | while (!DirectoriesToRemove->empty()) { | 
| Reid Spencer | 0c6a283 | 2004-11-05 22:15:36 +0000 | [diff] [blame] | 114 | DirectoriesToRemove->back().destroyDirectory(true); | 
| Reid Spencer | 3d7a614 | 2004-08-29 19:22:48 +0000 | [diff] [blame] | 115 | DirectoriesToRemove->pop_back(); | 
|  | 116 | } | 
|  | 117 |  | 
|  | 118 | if (std::find(IntSigs, IntSigsEnd, Sig) != IntSigsEnd) | 
|  | 119 | exit(1);   // If this is an interrupt signal, exit the program | 
|  | 120 |  | 
|  | 121 | // Otherwise if it is a fault (like SEGV) output the stacktrace to | 
|  | 122 | // STDERR (if we can) and reissue the signal to die... | 
|  | 123 | PrintStackTrace(); | 
|  | 124 | signal(Sig, SIG_DFL); | 
|  | 125 | } | 
|  | 126 |  | 
|  | 127 | // Just call signal | 
|  | 128 | void RegisterHandler(int Signal) { | 
|  | 129 | signal(Signal, SignalHandler); | 
|  | 130 | } | 
|  | 131 |  | 
|  | 132 | } | 
|  | 133 |  | 
|  | 134 | namespace llvm { | 
|  | 135 |  | 
|  | 136 | // RemoveFileOnSignal - The public API | 
| Reid Spencer | 26e2bd4 | 2004-11-14 22:09:22 +0000 | [diff] [blame] | 137 | void sys::RemoveFileOnSignal(const sys::Path &Filename) { | 
| Reid Spencer | 3d7a614 | 2004-08-29 19:22:48 +0000 | [diff] [blame] | 138 | if (FilesToRemove == 0) | 
|  | 139 | FilesToRemove = new std::vector<std::string>; | 
|  | 140 |  | 
| Reid Spencer | 26e2bd4 | 2004-11-14 22:09:22 +0000 | [diff] [blame] | 141 | FilesToRemove->push_back(Filename.get()); | 
| Reid Spencer | 3d7a614 | 2004-08-29 19:22:48 +0000 | [diff] [blame] | 142 |  | 
|  | 143 | std::for_each(IntSigs, IntSigsEnd, RegisterHandler); | 
|  | 144 | std::for_each(KillSigs, KillSigsEnd, RegisterHandler); | 
|  | 145 | } | 
|  | 146 |  | 
|  | 147 | // RemoveDirectoryOnSignal - The public API | 
|  | 148 | void sys::RemoveDirectoryOnSignal(const llvm::sys::Path& path) { | 
| Reid Spencer | 0c6a283 | 2004-11-05 22:15:36 +0000 | [diff] [blame] | 149 | if (!path.isDirectory()) | 
| Reid Spencer | 3d7a614 | 2004-08-29 19:22:48 +0000 | [diff] [blame] | 150 | return; | 
|  | 151 |  | 
|  | 152 | if (DirectoriesToRemove == 0) | 
|  | 153 | DirectoriesToRemove = new std::vector<sys::Path>; | 
|  | 154 |  | 
|  | 155 | DirectoriesToRemove->push_back(path); | 
|  | 156 |  | 
|  | 157 | std::for_each(IntSigs, IntSigsEnd, RegisterHandler); | 
|  | 158 | std::for_each(KillSigs, KillSigsEnd, RegisterHandler); | 
|  | 159 | } | 
|  | 160 |  | 
|  | 161 | /// PrintStackTraceOnErrorSignal - When an error signal (such as SIBABRT or | 
|  | 162 | /// SIGSEGV) is delivered to the process, print a stack trace and then exit. | 
|  | 163 | void sys::PrintStackTraceOnErrorSignal() { | 
|  | 164 | std::for_each(KillSigs, KillSigsEnd, RegisterHandler); | 
|  | 165 | } | 
|  | 166 |  | 
|  | 167 | } | 
|  | 168 |  | 
|  | 169 | // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab |