llvm/lib/System/Unix/Signals.inc
Chris Lattner 922a881f32 When a crash signal is delivered do two things: remove all of our
signal handlers to prevent reentrance on unrelated things (a sigabort
where the handle bus errors) also, clear the signal mask so that the
signal doesn't infinitely reissue.  This fixes rdar://6654827 -
Crash causes clang to loop



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@66330 91177308-0d34-0410-b5e6-96231b3b80d8
2009-03-07 08:15:47 +00:00

194 lines
6.0 KiB
C++

//===- Signals.cpp - Generic Unix Signals Implementation -----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines some helpful functions for dealing with the possibility of
// Unix signals occuring while your program is running.
//
//===----------------------------------------------------------------------===//
#include "Unix.h"
#include "llvm/ADT/STLExtras.h"
#include <vector>
#include <algorithm>
#if HAVE_EXECINFO_H
# include <execinfo.h> // For backtrace().
#endif
#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;
/// InterruptFunction - The function to call if ctrl-c is pressed.
static void (*InterruptFunction)() = 0;
static std::vector<sys::Path> *FilesToRemove = 0;
static std::vector<std::pair<void(*)(void*), void*> > *CallBacksToRun = 0;
// IntSigs - Signals that may interrupt the program at any time.
static const int IntSigs[] = {
SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2
};
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.
static const int KillSigs[] = {
SIGILL, SIGTRAP, SIGABRT, SIGFPE, SIGBUS, SIGSEGV, SIGSYS, SIGXCPU, SIGXFSZ
#ifdef SIGEMT
, SIGEMT
#endif
};
static const int *const KillSigsEnd =
KillSigs + sizeof(KillSigs) / sizeof(KillSigs[0]);
static void UnregisterHandler(int Signal) {
signal(Signal, SIG_DFL);
}
// 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.
std::for_each(KillSigs, KillSigsEnd, UnregisterHandler);
// Unmask all potentially blocked kill signals.
sigset_t SigMask;
sigfillset(&SigMask);
sigprocmask(SIG_UNBLOCK, &SigMask, 0);
if (FilesToRemove != 0)
while (!FilesToRemove->empty()) {
FilesToRemove->back().eraseFromDisk(true);
FilesToRemove->pop_back();
}
if (std::find(IntSigs, IntSigsEnd, Sig) != IntSigsEnd) {
if (InterruptFunction) {
void (*IF)() = InterruptFunction;
InterruptFunction = 0;
IF(); // run the interrupt function.
return;
}
exit(1); // If this is an interrupt signal, exit the program
}
// Otherwise if it is a fault (like SEGV) run any handler.
if (CallBacksToRun)
for (unsigned i = 0, e = CallBacksToRun->size(); i != e; ++i)
(*CallBacksToRun)[i].first((*CallBacksToRun)[i].second);
}
// Just call signal
static void RegisterHandler(int Signal) {
signal(Signal, SignalHandler);
}
void sys::SetInterruptFunction(void (*IF)()) {
InterruptFunction = IF;
RegisterHandler(SIGINT);
}
// RemoveFileOnSignal - The public API
bool sys::RemoveFileOnSignal(const sys::Path &Filename, std::string* ErrMsg) {
if (FilesToRemove == 0)
FilesToRemove = new std::vector<sys::Path>();
FilesToRemove->push_back(Filename);
std::for_each(IntSigs, IntSigsEnd, RegisterHandler);
std::for_each(KillSigs, KillSigsEnd, RegisterHandler);
return false;
}
/// 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 sys::AddSignalHandler(void (*FnPtr)(void *), void *Cookie) {
if (CallBacksToRun == 0)
CallBacksToRun = new std::vector<std::pair<void(*)(void*), void*> >();
CallBacksToRun->push_back(std::make_pair(FnPtr, Cookie));
std::for_each(KillSigs, KillSigsEnd, RegisterHandler);
}
// 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;
}
for (int i = 0; i < depth; ++i) {
Dl_info dlinfo;
dladdr(StackTrace[i], &dlinfo);
fprintf(stderr, "%-3d", i);
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() {
AddSignalHandler(PrintStackTrace, 0);
}