#include <stdio.h>
#include <string.h>
#include <limits.h>
#ifdef __GLIBC__
#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
#define HAVE_BACKTRACE 1
#include <execinfo.h>
#endif
#endif
#include "stdsage.h"
#include "interrupt.h"
struct sage_signals_t _signals;
static sigset_t default_sigmask;
static sigset_t sigmask_with_sigint;
#if defined(__i386__) || defined(__x86_64__)
#define CPU_ARCH_x86
static int cpu_has_emms = 0;
#endif
static inline void reset_CPU()
{
#ifdef CPU_ARCH_x86
if (cpu_has_emms)
{
asm("emms");
}
else
{
asm("ffree %st(0)");
asm("ffree %st(1)");
asm("ffree %st(2)");
asm("ffree %st(3)");
asm("ffree %st(4)");
asm("ffree %st(5)");
asm("ffree %st(6)");
asm("ffree %st(7)");
}
#endif
}
void sage_interrupt_handler(int sig)
{
#if ENABLE_DEBUG_INTERRUPT
fprintf(stderr, "\n*** SIGINT *** %s sig_on\n", (_signals.sig_on_count > 0) ? "inside" : "outside");
print_backtrace();
#endif
if (_signals.sig_on_count > 0)
{
if (_signals.block_sigint)
{
_signals.interrupt_received = 1;
return;
}
PyErr_SetNone(PyExc_KeyboardInterrupt);
reset_CPU();
siglongjmp(_signals.env, sig);
}
else
{
PyErr_SetInterrupt();
_signals.interrupt_received = 1;
}
}
void call_sage_interrupt_handler(int sig)
{
sigprocmask(SIG_BLOCK, &sigmask_with_sigint, NULL);
sage_interrupt_handler(sig);
}
void sage_signal_handler(int sig)
{
sig_atomic_t inside = _signals.inside_signal_handler;
_signals.inside_signal_handler = 1;
if (inside == 0 && _signals.sig_on_count > 0)
{
const char* msg = _signals.s;
if (!msg)
{
switch(sig)
{
case SIGILL: msg = "Illegal instruction"; break;
case SIGABRT: msg = "Aborted"; break;
case SIGFPE: msg = "Floating point exception"; break;
case SIGBUS: msg = "Bus error"; break;
case SIGSEGV: msg = "Segmentation fault"; break;
default: msg = "";
}
}
PyErr_SetString(PyExc_RuntimeError, msg);
reset_CPU();
siglongjmp(_signals.env, sig);
}
else
{
signal(SIGILL, SIG_DFL);
signal(SIGABRT, SIG_DFL);
signal(SIGFPE, SIG_DFL);
signal(SIGBUS, SIG_DFL);
signal(SIGSEGV, SIG_DFL);
sigprocmask(SIG_SETMASK, &sigmask_with_sigint, NULL);
if (inside) sigdie(sig, "An error occured during signal handling.");
switch(sig)
{
case SIGILL:
sigdie(sig, "Unhandled SIGILL: An illegal instruction occurred in Sage.");
break;
case SIGABRT:
sigdie(sig, "Unhandled SIGABRT: An abort() occurred in Sage.");
break;
case SIGFPE:
sigdie(sig, "Unhandled SIGFPE: An unhandled floating point exception occurred in Sage.");
break;
case SIGBUS:
sigdie(sig, "Unhandled SIGBUS: A bus error occurred in Sage.");
break;
case SIGSEGV:
sigdie(sig, "Unhandled SIGSEGV: A segmentation fault occurred in Sage.");
break;
};
sigdie(sig, "Unknown signal received.\n");
}
}
int _sig_on_interrupt_received()
{
_signals.interrupt_received = 0;
if (PyErr_CheckSignals())
{
_signals.sig_on_count = 0;
return 0;
}
return 1;
}
void _sig_on_recover()
{
_signals.block_sigint = 0;
_signals.sig_on_count = 0;
sigprocmask(SIG_SETMASK, &default_sigmask, NULL);
_signals.inside_signal_handler = 0;
}
void _sig_off_warning(const char* file, int line)
{
char buf[320];
snprintf(buf, sizeof(buf), "sig_off() without sig_on() at %s:%i", file, line);
PyErr_WarnEx(PyExc_RuntimeWarning, buf, 2);
print_backtrace();
}
void set_sage_signal_handler_message(const char* s)
{
_signals.s = s;
}
void setup_sage_signal_handler()
{
memset(&_signals, 0, sizeof(_signals));
sigprocmask(SIG_BLOCK, NULL, &default_sigmask);
sigprocmask(SIG_BLOCK, NULL, &sigmask_with_sigint);
sigaddset(&sigmask_with_sigint, SIGINT);
sigaddset(&sigmask_with_sigint, SIGALRM);
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGINT);
sigaddset(&sa.sa_mask, SIGALRM);
sa.sa_handler = sage_interrupt_handler;
if (sigaction(SIGINT, &sa, NULL)) {perror("sigaction"); exit(1);}
sa.sa_handler = sage_signal_handler;
sa.sa_flags |= SA_NODEFER;
if (sigaction(SIGILL, &sa, NULL)) {perror("sigaction"); exit(1);}
if (sigaction(SIGABRT, &sa, NULL)) {perror("sigaction"); exit(1);}
if (sigaction(SIGFPE, &sa, NULL)) {perror("sigaction"); exit(1);}
if (sigaction(SIGBUS, &sa, NULL)) {perror("sigaction"); exit(1);}
if (sigaction(SIGSEGV, &sa, NULL)) {perror("sigaction"); exit(1);}
#ifdef CPU_ARCH_x86
if (!cpu_has_emms)
{
if (sig_on_no_except())
{
asm("emms");
sig_off();
cpu_has_emms = 1;
}
else
{
PyErr_Clear();
}
}
#endif
}
void print_backtrace()
{
void* backtracebuffer[1024];
fflush(stderr);
#ifdef HAVE_BACKTRACE
int btsize = backtrace(backtracebuffer, 1024);
backtrace_symbols_fd(backtracebuffer, btsize, 2);
#endif
}
void sigdie(int sig, const char* s)
{
print_backtrace();
fprintf(stderr, "\n"
"------------------------------------------------------------------------\n"
"%s\n"
"This probably occurred because a *compiled* component of Sage has a bug\n"
"in it and is not properly wrapped with sig_on(), sig_off(). You might\n"
"want to run Sage under gdb with 'sage -gdb' to debug this.\n"
"Sage will now terminate.\n"
"------------------------------------------------------------------------\n",
s);
fflush(stderr);
kill(getpid(), sig);
exit(128 + sig);
}