Add a new 'AddSignalHandler' function to Signals.h that allows

arbitrary functions to be run when a crash happens.  Delete 
RemoveDirectoryOnSignal as it is dead and has never had clients.

Change PrintStackTraceOnErrorSignal to be implemented in terms of
AddSignalHandler.

I updated the Win32 versions of these APIs, but can't test them.
If there are any problems, I'd be happy to fix them as well.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@66072 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Chris Lattner 2009-03-04 21:21:36 +00:00
parent fd0339933b
commit 35033a5876
3 changed files with 88 additions and 141 deletions

View File

@ -25,17 +25,16 @@ namespace sys {
/// @brief Remove a file if a fatal signal occurs.
bool RemoveFileOnSignal(const Path &Filename, std::string* ErrMsg = 0);
/// This function registers a signal handler to ensure that if a fatal signal
/// gets delivered to the process that the named directory and all its
/// contents are removed.
/// @brief Remove a directory if a fatal signal occurs.
bool RemoveDirectoryOnSignal(const Path& path, std::string* ErrMsg = 0);
/// When an error signal (such as SIBABRT or SIGSEGV) is delivered to the
/// process, print a stack trace and then exit.
/// @brief Print a stack trace if a fatal signal occurs.
void PrintStackTraceOnErrorSignal();
/// AddSignalHandler - Add a function to be called when an abort/kill 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 AddSignalHandler(void (*FnPtr)(void *), void *Cookie);
/// This function registers a function to be called when the user "interrupts"
/// the program (typically by pressing ctrl-c). When the user interrupts the
/// program, the specified interrupt function is called instead of the program

View File

@ -31,15 +31,11 @@
#endif
using namespace llvm;
namespace {
static bool StackTraceRequested = false;
/// InterruptFunction - The function to call if ctrl-c is pressed.
static void (*InterruptFunction)() = 0;
static std::vector<sys::Path> *FilesToRemove = 0 ;
static std::vector<sys::Path> *DirectoriesToRemove = 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[] = {
@ -59,17 +55,77 @@ static const int KillSigs[] = {
static const int *const KillSigsEnd =
KillSigs + sizeof(KillSigs) / sizeof(KillSigs[0]);
#ifdef HAVE_BACKTRACE
static void* StackTrace[256];
#endif
// SignalHandler - The signal handler that runs...
static RETSIGTYPE SignalHandler(int Sig) {
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);
// Restore the signal behavior to default, so that the program actually
// crashes when we return and the signal reissues.
signal(Sig, SIG_DFL);
}
// 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() {
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)));
@ -118,91 +174,9 @@ static void PrintStackTrace() {
#endif
}
// SignalHandler - The signal handler that runs...
static RETSIGTYPE SignalHandler(int Sig) {
if (FilesToRemove != 0)
while (!FilesToRemove->empty()) {
FilesToRemove->back().eraseFromDisk(true);
FilesToRemove->pop_back();
}
if (DirectoriesToRemove != 0)
while (!DirectoriesToRemove->empty()) {
DirectoriesToRemove->back().eraseFromDisk(true);
DirectoriesToRemove->pop_back();
}
if (std::find(IntSigs, IntSigsEnd, Sig) != IntSigsEnd) {
if (InterruptFunction) {
void (*IF)() = InterruptFunction;
InterruptFunction = 0;
IF(); // run the interrupt function.
return;
} else {
exit(1); // If this is an interrupt signal, exit the program
}
}
// 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);
}
// 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;
}
// RemoveDirectoryOnSignal - The public API
bool sys::RemoveDirectoryOnSignal(const sys::Path& path, std::string* ErrMsg) {
// Not a directory?
struct stat buf;
if (0 != stat(path.c_str(), &buf)) {
MakeErrMsg(ErrMsg, path.toString() + ": can't get status of file");
return true;
}
if (!S_ISDIR(buf.st_mode)) {
if (ErrMsg)
*ErrMsg = path.toString() + " is not a directory";
return true;
}
if (DirectoriesToRemove == 0)
DirectoriesToRemove = new std::vector<sys::Path>;
DirectoriesToRemove->push_back(path);
std::for_each(IntSigs, IntSigsEnd, RegisterHandler);
std::for_each(KillSigs, KillSigsEnd, RegisterHandler);
return false;
}
/// 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);
AddSignalHandler(PrintStackTrace, 0);
}

View File

@ -39,7 +39,7 @@ static BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType);
static void (*InterruptFunction)() = 0;
static std::vector<llvm::sys::Path> *FilesToRemove = NULL;
static std::vector<llvm::sys::Path> *DirectoriesToRemove = NULL;
static std::vector<std::pair<void(*)(void*), void*> > *CallBacksToRun = 0;
static bool RegisteredUnhandledExceptionFilter = false;
static bool CleanupExecuted = false;
static PTOP_LEVEL_EXCEPTION_FILTER OldFilter = NULL;
@ -98,37 +98,6 @@ bool sys::RemoveFileOnSignal(const sys::Path &Filename, std::string* ErrMsg) {
return false;
}
// RemoveDirectoryOnSignal - The public API
bool sys::RemoveDirectoryOnSignal(const sys::Path& path, std::string* ErrMsg) {
// Not a directory?
WIN32_FILE_ATTRIBUTE_DATA fi;
if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) {
MakeErrMsg(ErrMsg, path.toString() + ": can't get status of file");
return true;
}
if (!(fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
if (ErrMsg)
*ErrMsg = path.toString() + ": not a directory";
return true;
}
RegisterHandler();
if (CleanupExecuted) {
if (ErrMsg)
*ErrMsg = "Process terminating -- cannot register for removal";
return true;
}
if (DirectoriesToRemove == NULL)
DirectoriesToRemove = new std::vector<sys::Path>;
DirectoriesToRemove->push_back(path);
LeaveCriticalSection(&CriticalSection);
return false;
}
/// 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() {
@ -162,14 +131,9 @@ static void Cleanup() {
FilesToRemove->pop_back();
}
if (DirectoriesToRemove != NULL)
while (!DirectoriesToRemove->empty()) {
try {
DirectoriesToRemove->back().eraseFromDisk(true);
} catch (...) {
}
DirectoriesToRemove->pop_back();
}
if (CallBacksToRun)
for (unsigned i = 0, e = CallBacksToRun->size(); i != e; ++i)
(*CallBacksToRun)[i].first((*CallBacksToRun)[i].second);
LeaveCriticalSection(&CriticalSection);
}
@ -259,7 +223,7 @@ static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) {
#endif
} catch (...) {
assert(!"Crashed in LLVMUnhandledExceptionFilter");
assert(0 && "Crashed in LLVMUnhandledExceptionFilter");
}
// Allow dialog box to pop up allowing choice to start debugger.
@ -292,3 +256,13 @@ static BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType) {
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);
}