1
0
mirror of https://github.com/stefanocasazza/ULib.git synced 2025-09-28 19:05:55 +08:00
ULib/src/ulib/utility/interrupt.cpp
2017-05-02 15:23:41 +02:00

550 lines
16 KiB
C++

// ============================================================================
//
// = LIBRARY
// ULib - c++ library
//
// = FILENAME
// interrupt.cpp
//
// = AUTHOR
// Stefano Casazza
//
// ============================================================================
#include <ulib/base/utility.h>
#include <ulib/utility/interrupt.h>
/*
const char* UInterrupt::ILL_errlist[] = {
"ILL_ILLOPC", "illegal opcode", // ILL_ILLOPC 1
"ILL_ILLOPN", "illegal operand", // ILL_ILLOPN 2
"ILL_ILLADR", "illegal addressing mode", // ILL_ILLADR 3
"ILL_ILLTRP", "illegal trap", // ILL_ILLTRP 4
"ILL_PRVOPC", "privileged opcode", // ILL_PRVOPC 5
"ILL_PRVREG", "privileged register", // ILL_PRVREG 6
"ILL_COPROC", "coprocessor error", // ILL_COPROC 7
"ILL_BADSTK", "internal stack error" // ILL_BADSTK 8
};
const char* UInterrupt::FPE_errlist[] = {
"FPE_INTOVF", "integer overflow", // FPE_INTOVF 2
"FPE_FLTDIV", "floating point divide by zero", // FPE_FLTDIV 3
"FPE_FLTOVF", "floating point overflow", // FPE_FLTOVF 4
"FPE_FLTUND", "floating point underflow", // FPE_FLTUND 5
"FPE_FLTRES", "floating point inexact result", // FPE_FLTRES 6
"FPE_FLTINV", "floating point invalid operation", // FPE_FLTINV 7
"FPE_FLTSUB", "subscript out of range" // FPE_FLTSUB 8
};
const char* UInterrupt::CLD_list[] = {
"CLD_EXITED", "child has exited", // CLD_EXITED 1
"CLD_KILLED", "child was killed", // CLD_KILLED 2
"CLD_DUMPED", "child terminated abnormally", // CLD_DUMPED 3
"CLD_TRAPPED", "traced child has trapped", // CLD_TRAPPED 4
"CLD_STOPPED", "child has stopped", // CLD_STOPPED 5
"CLD_CONTINUED", "stopped child has continued", // CLD_CONTINUED 6
};
const char* UInterrupt::POLL_list[] = {
"POLL_IN", "data input available", // POLL_IN 1
"POLL_OUT", "output buffers available", // POLL_OUT 2
"POLL_MSG", "input message available", // POLL_MSG 3
"POLL_ERR", "i/o error", // POLL_ERR 4
"POLL_PRI", "high priority input available", // POLL_PRI 5
"POLL_HUP", "device disconnected" // POLL_HUP 6
};
const char* UInterrupt::TRAP_list[] = {
"TRAP_BRKPT", "process breakpoint", // TRAP_BRKPT 1
"TRAP_TRACE", "process trace trap", // TRAP_TRACE 2
};
const char* UInterrupt::origin_list[] = {
"SI_USER", "kill(), sigsend() or raise()", // SI_USER 0
"SI_QUEUE", "sigqueue()", // SI_QUEUE -1
"SI_TIMER", "timer expired", // SI_TIMER -2
"SI_MESGQ", "mesq state changed", // SI_MESGQ -3
"SI_ASYNCIO", "AIO completed", // SI_ASYNCIO -4
"SI_SIGIO", "queued SIGIO", // SI_SIGIO -5
"SI_KERNEL", "kernel" // SI_KERNEL 0x80
};
*/
const char* UInterrupt::SEGV_errlist[] = {
"", "",
"SEGV_MAPERR", "Address not mapped to object", // SEGV_MAPERR 1
"SEGV_ACCERR", "Invalid permissions for mapped object" // SEGV_ACCERR 2
};
const char* UInterrupt::BUS_errlist[] = {
"", "",
"BUS_ADRALN", "Invalid address alignment", // BUS_ADRALN 1
"BUS_ADRERR", "Non-existant physical address", // BUS_ADRERR 2
"BUS_OBJERR", "Object specific hardware error" // BUS_OBJERR 3
};
bool UInterrupt::flag_alarm;
bool UInterrupt::syscall_restart; // NB: notify to make certain system calls restartable across signals...
bool UInterrupt::exit_loop_wait_event_for_signal;
jmp_buf UInterrupt::jbuf;
sigset_t* UInterrupt::mask_interrupt;
sig_atomic_t UInterrupt::flag_wait_for_signal;
struct sigaction UInterrupt::act;
struct itimerval UInterrupt::timerval;
struct sigaction UInterrupt::old[NSIG];
int UInterrupt::event_signal_pending;
sig_atomic_t UInterrupt::event_signal[NSIG];
sighandler_t UInterrupt::handler_signal[NSIG];
void UInterrupt::insert(int signo, sighandler_t handler)
{
U_TRACE(1, "UInterrupt::insert(%d,%p)", signo, handler)
U_INTERNAL_ASSERT_RANGE(1, signo, NSIG)
handler_signal[signo] = handler;
act.sa_handler = handlerEventSignal;
(void) U_SYSCALL(sigaction, "%d,%p,%p", signo, &act, old + signo);
}
void UInterrupt::erase(int signo)
{
U_TRACE(1, "UInterrupt::erase(%d)", signo)
U_INTERNAL_ASSERT_RANGE(1, signo, NSIG)
U_INTERNAL_ASSERT_POINTER(handler_signal[signo])
handler_signal[signo] = U_NULLPTR;
(void) U_SYSCALL(sigaction, "%d,%p,%p", signo, old + signo, U_NULLPTR);
}
bool UInterrupt::disable(sigset_t* mask, sigset_t* mask_old)
{
U_TRACE(1, "UInterrupt::disable(%p,%p)", mask, mask_old)
if (!mask)
{
if (!mask_interrupt) setMaskInterrupt(U_NULLPTR, 0);
mask = mask_interrupt;
}
if (U_SYSCALL(sigprocmask, "%d,%p,%p", SIG_BLOCK, mask, mask_old) == 0) U_RETURN(true);
U_RETURN(false);
}
bool UInterrupt::enable(sigset_t* mask)
{
U_TRACE(1, "UInterrupt::enable(%p)", mask)
if (!mask)
{
if (!mask_interrupt) setMaskInterrupt(U_NULLPTR, 0);
mask = mask_interrupt;
}
if (U_SYSCALL(sigprocmask, "%d,%p,%p", SIG_BLOCK, mask, U_NULLPTR) == 0) U_RETURN(true);
U_RETURN(false);
}
RETSIGTYPE UInterrupt::handlerSignal(int signo)
{
U_TRACE(0, "[SIGNAL] UInterrupt::handlerSignal(%d)", signo)
flag_wait_for_signal = false;
}
// Send ourselves the signal: see http://www.cons.org/cracauer/sigint.html
void UInterrupt::sendOurselves(int signo)
{
U_TRACE(0, "UInterrupt::sendOurselves(%d)", signo)
setHandlerForSignal(signo, (sighandler_t)SIG_DFL);
u_exit();
(void) U_SYSCALL(kill, "%d,%d", u_pid, signo);
}
RETSIGTYPE UInterrupt::handlerInterrupt(int signo)
{
U_TRACE(0, "UInterrupt::handlerInterrupt(%d)", signo)
U_MESSAGE("(pid %P) program interrupt - %Y", signo);
if (signo == SIGBUS || // 7
signo == SIGSEGV) // 11
{
# ifdef DEBUG
u_debug_at_exit();
# endif
}
// U_EXIT(-1);
sendOurselves(signo);
}
RETSIGTYPE UInterrupt::handlerEventSignal(int signo)
{
#ifdef DEBUG
u_trace_unlock();
#endif
U_TRACE(0, "[SIGNAL] UInterrupt::handlerEventSignal(%d)", signo)
++event_signal[signo];
event_signal_pending = (event_signal_pending ? NSIG : signo);
}
void UInterrupt::waitForSignal(int signo)
{
U_TRACE(1, "UInterrupt::waitForSignal(%d)", signo)
static sigset_t mask_wait_for_signal;
flag_wait_for_signal = true;
setHandlerForSignal(signo, (sighandler_t)handlerSignal);
while (flag_wait_for_signal == true)
{
(void) U_SYSCALL(sigsuspend, "%p", &mask_wait_for_signal);
}
}
void UInterrupt::callHandlerSignal()
{
U_TRACE_NO_PARAM(0, "UInterrupt::callHandlerSignal()")
int i;
loop:
U_INTERNAL_DUMP("event_signal_pending = %d", event_signal_pending)
i = event_signal_pending;
event_signal_pending = 0;
if (i < NSIG)
{
U_INTERNAL_ASSERT_POINTER(handler_signal[i])
handler_signal[i](event_signal[i]);
event_signal[i] = 0;
}
else
{
for (i = 1; i < NSIG; ++i)
{
if (event_signal[i])
{
U_INTERNAL_ASSERT_POINTER(handler_signal[i])
handler_signal[i](event_signal[i]);
event_signal[i] = 0;
}
}
}
// NB: it can happen that in manage the signal the calling function produce another signal because
// the interval is too short (< 10ms) in this case the parameter to the calling function is zero...
if (event_signal_pending) goto loop;
}
void UInterrupt::setMaskInterrupt(sigset_t* mask, int signo)
{
U_TRACE(1, "UInterrupt::setMaskInterrupt(%p,%d)", mask, signo)
if (mask)
{
# ifdef sigemptyset
sigemptyset(mask);
# else
(void) U_SYSCALL(sigemptyset, "%p", mask);
# endif
# ifdef sigaddset
sigaddset(mask, signo);
# else
(void) U_SYSCALL(sigaddset, "%p,%d", mask, signo);
# endif
}
else
{
U_INTERNAL_ASSERT_EQUALS(mask_interrupt, U_NULLPTR)
mask_interrupt = new sigset_t;
# ifdef sigemptyset
sigemptyset(mask_interrupt);
# else
(void) U_SYSCALL(sigemptyset, "%p", mask_interrupt);
# endif
# ifdef sigaddset
sigaddset(mask_interrupt, SIGUSR1); // 10
sigaddset(mask_interrupt, SIGUSR2); // 12
sigaddset(mask_interrupt, SIGALRM); // 14
sigaddset(mask_interrupt, SIGCHLD); // 17
# else
(void) U_SYSCALL(sigaddset, "%p,%d", mask_interrupt, SIGUSR1); // 10
(void) U_SYSCALL(sigaddset, "%p,%d", mask_interrupt, SIGUSR2); // 12
(void) U_SYSCALL(sigaddset, "%p,%d", mask_interrupt, SIGALRM); // 14
(void) U_SYSCALL(sigaddset, "%p,%d", mask_interrupt, SIGCHLD); // 17
# endif
}
}
void UInterrupt::init()
{
U_TRACE_NO_PARAM(1, "UInterrupt::init()")
#ifdef HAVE_SIGINFO_T
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = handlerInterruptWithInfo;
(void) U_SYSCALL(sigaction, "%d,%p,%p", SIGBUS, &act, U_NULLPTR); // 7
(void) U_SYSCALL(sigaction, "%d,%p,%p", SIGSEGV, &act, U_NULLPTR); // 11
/*
(void) U_SYSCALL(sigaction, "%d,%p,%p", SIGCHLD, &act, U_NULLPTR);
(void) U_SYSCALL(sigaction, "%d,%p,%p", SIGILL, &act, U_NULLPTR);
(void) U_SYSCALL(sigaction, "%d,%p,%p", SIGFPE, &act, U_NULLPTR);
(void) U_SYSCALL(sigaction, "%d,%p,%p", SIGPOLL, &act, U_NULLPTR);
(void) U_SYSCALL(sigaction, "%d,%p,%p", SIGTRAP, &act, U_NULLPTR);
*/
#endif
act.sa_flags = 0;
act.sa_handler = handlerInterrupt;
// (void) U_SYSCALL(sigaction, "%d,%p,%p", SIGHUP, &act, U_NULLPTR);
(void) U_SYSCALL(sigaction, "%d,%p,%p", SIGINT, &act, U_NULLPTR); // 2
// (void) U_SYSCALL(sigaction, "%d,%p,%p", SIGQUIT, &act, U_NULLPTR);
(void) U_SYSCALL(sigaction, "%d,%p,%p", SIGTERM, &act, U_NULLPTR); // 15
#ifndef _MSWINDOWS_
// (void) U_SYSCALL(sigaction, "%d,%p,%p", SIGPIPE, &act, U_NULLPTR); // 13
#endif
#ifndef HAVE_SIGINFO_T
(void) U_SYSCALL(sigaction, "%d,%p,%p", SIGSEGV, &act, U_NULLPTR); // 11
# ifndef _MSWINDOWS_
(void) U_SYSCALL(sigaction, "%d,%p,%p", SIGBUS, &act, U_NULLPTR); // 7
# endif
#endif
#ifndef _MSWINDOWS_
sigset_t mask_sigpipe;
setMaskInterrupt(&mask_sigpipe, SIGPIPE);
disable(&mask_sigpipe, U_NULLPTR);
#endif
syscall_restart = true; // NB: notify to make certain system calls restartable across signals...
setHandlerForSignal(SIGALRM, (sighandler_t)handlerAlarm);
}
RETSIGTYPE UInterrupt::handlerAlarm(int signo)
{
U_TRACE(0, "[SIGALRM] UInterrupt::handlerAlarm(%d)", signo)
if ((flag_alarm = (signo == SIGALRM)))
{
timerval.it_value.tv_sec =
timerval.it_value.tv_usec = 0;
}
U_INTERNAL_DUMP("timerval.it_value = { %ld %6ld }", timerval.it_value.tv_sec, timerval.it_value.tv_usec)
(void) U_SYSCALL(setitimer, "%d,%p,%p", ITIMER_REAL, &timerval, U_NULLPTR);
}
void UInterrupt::setHandlerForSegv(vPF func, bPF fault)
{
U_TRACE(0+256, "UInterrupt::setHandlerForSegv(%p,%p)", func, fault)
#ifndef HAVE_SIGINFO_T
act.sa_handler = handlerSegv;
#else
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = handlerSegvWithInfo;
#endif
(void) U_SYSCALL(sigaction, "%d,%p,%p", SIGSEGV, &act, old + SIGSEGV); // 11
retry:
if (setjmp(jbuf) == 0) func();
else
{
U_WARNING("%.*s", u_buffer_len, u_buffer);
u_buffer_len = 0;
if (fault()) goto retry;
}
(void) U_SYSCALL(sigaction, "%d,%p,%p", SIGSEGV, old + SIGSEGV, U_NULLPTR);
}
RETSIGTYPE UInterrupt::handlerSegvWithInfo(int signo, siginfo_t* info, void* context)
{
if (u_recursion == false) // NB: maybe recursion occurs...
{
# ifdef DEBUG
u_flag_test = 0;
U_TRACE(0, "[SIGSEGV] UInterrupt::handlerSegvWithInfo(%d,%p,%p)", signo, info, context)
# endif
getSignalInfo(signo, info);
/**
* When you do something that causes the kernel to send you a SIGSEGV:
*
* If you don't have a signal handler, the kernel kills the process and that's that
*
* If you do have a signal handler
* Your handler gets called
* ==> The kernel restarts the offending operation <==
*
* So if you don't do anything abut it, it will just loop continuously.
* If you do catch SIGSEGV and you don't exit, thereby interfering with
* the normal flow, you must:
*
* fix things such that the offending operation doesn't restart or
* fix the memory layout such that what was offending will be ok on the next run
*/
longjmp(jbuf, 1);
}
::abort();
}
void UInterrupt::getSignalInfo(int signo, siginfo_t* info)
{
U_TRACE(0, "UInterrupt::getSignalInfo(%d,%p)", signo, info)
/*
#if defined(__CYGWIN__)
union sigval {
int sival_int; // Integer signal value
void* sival_ptr; // Pointer signal value
}
siginfo_t {
int si_signo; // Signal number
int si_code; // Cause of the signal
union sigval si_value; // Signal value
}
#else
siginfo_t {
int si_signo; // Signal number
int si_errno; // An errno value
int si_code; // Signal code
pid_t si_pid; // Sending process ID
uid_t si_uid; // Real user ID of sending process
int si_status; // Exit value or signal
clock_t si_utime; // User time consumed
clock_t si_stime; // System time consumed
sigval_t si_value; // Signal value
int si_int; // POSIX.1b signal
void* si_ptr; // POSIX.1b signal
void* si_addr; // Memory location which caused fault
int si_band; // Band event
int si_fd; // File descriptor
}
#endif
*/
#ifndef SI_FROMKERNEL
# ifdef _MSWINDOWS_
# define SI_FROMKERNEL(siptr) false
# else
# define SI_FROMKERNEL(siptr) ((siptr)->si_code > 0)
# endif
#endif
#ifndef _MSWINDOWS_
# ifdef HAVE_MEMBER_SI_ADDR
# define U_MSG_FROMKERNEL \
"program interrupt by the kernel - %Y%W\n" \
"----------------------------------------" \
"----------------------------------------------------\n" \
" pid: %W%P%W\n" \
" address: %W%p - %s (%d, %s)%W\n" \
" rss usage: %W%.2f MBytes%W\n" \
" address space usage: %W%.2f MBytes%W\n" \
"----------------------------------------" \
"----------------------------------------------------"
# else
# define U_MSG_FROMKERNEL \
"program interrupt by the kernel - %Y%W\n" \
"----------------------------------------" \
"----------------------------------------------------\n" \
" pid: %W%P%W\n" \
" desc: %W %s (%d, %s)%W\n" \
" rss usage: %W%.2f MBytes%W\n" \
" address space usage: %W%.2f MBytes%W\n" \
"----------------------------------------" \
"----------------------------------------------------"
# endif
#endif
if (info == U_NULLPTR ||
SI_FROMKERNEL(info) == false)
{
u_buffer_len = u__snprintf(u_buffer, U_BUFFER_SIZE, U_CONSTANT_TO_PARAM("program interrupt by kill(), sigsend() or raise() - %Y"), signo);
}
#ifndef _MSWINDOWS_
else
{
unsigned long vsz, rss;
int index = info->si_code * 2;
const char** errlist = (signo == SIGBUS ? BUS_errlist : SEGV_errlist);
u_get_memusage(&vsz, &rss);
u_buffer_len = u__snprintf(u_buffer, U_BUFFER_SIZE, U_CONSTANT_TO_PARAM(U_MSG_FROMKERNEL),
signo, YELLOW,
CYAN, YELLOW,
# ifdef HAVE_MEMBER_SI_ADDR
CYAN, info->si_addr, errlist[index], info->si_code, errlist[index+1], YELLOW,
# else
CYAN, errlist[index], info->si_code, errlist[index+1], YELLOW,
# endif
CYAN, (double)vsz / (1024.0 * 1024.0), YELLOW,
CYAN, (double)rss / (1024.0 * 1024.0), YELLOW);
}
# endif
}
__noreturn RETSIGTYPE UInterrupt::handlerInterruptWithInfo(int signo, siginfo_t* info, void* context)
{
if (u_recursion == false) // NB: maybe recursion occurs...
{
# ifdef DEBUG
u_flag_test = 0;
U_TRACE(0, "UInterrupt::handlerInterruptWithInfo(%d,%p,%p)", signo, info, context)
# endif
getSignalInfo(signo, info);
U_ABORT("%.*s", u_buffer_len, u_buffer);
}
::abort();
}