static const size_t faulthandler_nsignals = \
Py_ARRAY_LENGTH(faulthandler_handlers);
-#ifdef HAVE_SIGALTSTACK
+/* Using an alternative stack requires sigaltstack()
+ and sigaction() SA_ONSTACK */
+#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION)
+# define FAULTHANDLER_USE_ALT_STACK
+#endif
+
+#ifdef FAULTHANDLER_USE_ALT_STACK
static stack_t stack;
static stack_t old_stack;
#endif
}
#endif
+
+#ifdef FAULTHANDLER_USE_ALT_STACK
+static int
+faulthandler_allocate_stack(void)
+{
+ if (stack.ss_sp != NULL) {
+ return 0;
+ }
+ /* Allocate an alternate stack for faulthandler() signal handler
+ to be able to execute a signal handler on a stack overflow error */
+ stack.ss_sp = PyMem_Malloc(stack.ss_size);
+ if (stack.ss_sp == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
+
+ int err = sigaltstack(&stack, &old_stack);
+ if (err) {
+ /* Release the stack to retry sigaltstack() next time */
+ PyMem_Free(stack.ss_sp);
+ stack.ss_sp = NULL;
+
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+
/* Install the handler for fatal signals, faulthandler_fatal_error(). */
static int
}
fatal_error.enabled = 1;
+#ifdef FAULTHANDLER_USE_ALT_STACK
+ if (faulthandler_allocate_stack() < 0) {
+ return -1;
+ }
+#endif
+
for (size_t i=0; i < faulthandler_nsignals; i++) {
fault_handler_t *handler;
-#ifdef HAVE_SIGACTION
- struct sigaction action;
-#endif
int err;
handler = &faulthandler_handlers[i];
assert(!handler->enabled);
#ifdef HAVE_SIGACTION
+ struct sigaction action;
action.sa_handler = faulthandler_fatal_error;
sigemptyset(&action.sa_mask);
/* Do not prevent the signal from being received from within
its own signal handler */
action.sa_flags = SA_NODEFER;
-#ifdef HAVE_SIGALTSTACK
- if (stack.ss_sp != NULL) {
- /* Call the signal handler on an alternate signal stack
- provided by sigaltstack() */
- action.sa_flags |= SA_ONSTACK;
- }
+#ifdef FAULTHANDLER_USE_ALT_STACK
+ assert(stack.ss_sp != NULL);
+ /* Call the signal handler on an alternate signal stack
+ provided by sigaltstack() */
+ action.sa_flags |= SA_ONSTACK;
#endif
err = sigaction(handler->signum, &action, &handler->previous);
#else
handler->previous = signal(handler->signum,
- faulthandler_fatal_error);
+ faulthandler_fatal_error);
err = (handler->previous == SIG_ERR);
#endif
if (err) {
}
tstate = get_thread_state();
- if (tstate == NULL)
+ if (tstate == NULL) {
return NULL;
+ }
fd = faulthandler_get_fileno(&file);
- if (fd < 0)
+ if (fd < 0) {
return NULL;
+ }
+
+ if (!thread.running) {
+ thread.running = PyThread_allocate_lock();
+ if (!thread.running) {
+ return PyErr_NoMemory();
+ }
+ }
+ if (!thread.cancel_event) {
+ thread.cancel_event = PyThread_allocate_lock();
+ if (!thread.cancel_event || !thread.running) {
+ return PyErr_NoMemory();
+ }
+
+ /* cancel_event starts to be acquired: it's only released to cancel
+ the thread. */
+ PyThread_acquire_lock(thread.cancel_event, 1);
+ }
/* format the timeout */
header = format_timeout(timeout_us);
- if (header == NULL)
+ if (header == NULL) {
return PyErr_NoMemory();
+ }
header_len = strlen(header);
/* Cancel previous thread, if running */
}
#endif /* FAULTHANDLER_LATER */
+
#ifdef FAULTHANDLER_USER
static int
-faulthandler_register(int signum, int chain, _Py_sighandler_t *p_previous)
+faulthandler_register(int signum, int chain, _Py_sighandler_t *previous_p)
{
#ifdef HAVE_SIGACTION
struct sigaction action;
own signal handler */
action.sa_flags = SA_NODEFER;
}
-#ifdef HAVE_SIGALTSTACK
- if (stack.ss_sp != NULL) {
- /* Call the signal handler on an alternate signal stack
- provided by sigaltstack() */
- action.sa_flags |= SA_ONSTACK;
- }
+#ifdef FAULTHANDLER_USE_ALT_STACK
+ assert(stack.ss_sp != NULL);
+ /* Call the signal handler on an alternate signal stack
+ provided by sigaltstack() */
+ action.sa_flags |= SA_ONSTACK;
#endif
- return sigaction(signum, &action, p_previous);
+ return sigaction(signum, &action, previous_p);
#else
_Py_sighandler_t previous;
previous = signal(signum, faulthandler_user);
- if (p_previous != NULL)
- *p_previous = previous;
+ if (previous_p != NULL) {
+ *previous_p = previous;
+ }
return (previous == SIG_ERR);
#endif
}
user = &user_signals[signum];
if (!user->enabled) {
+#ifdef FAULTHANDLER_USE_ALT_STACK
+ if (faulthandler_allocate_stack() < 0) {
+ return NULL;
+ }
+#endif
+
err = faulthandler_register(signum, chain, &previous);
if (err) {
PyErr_SetFromErrno(PyExc_OSError);
Py_RETURN_NONE;
}
-#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION)
+#if defined(FAULTHANDLER_USE_ALT_STACK)
#define FAULTHANDLER_STACK_OVERFLOW
#ifdef __INTEL_COMPILER
size, depth);
return NULL;
}
-#endif /* defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) */
+#endif /* defined(FAULTHANDLER_USE_ALT_STACK) && defined(HAVE_SIGACTION) */
static int
PyStatus
_PyFaulthandler_Init(int enable)
{
-#ifdef HAVE_SIGALTSTACK
- int err;
-
- /* Try to allocate an alternate stack for faulthandler() signal handler to
- * be able to allocate memory on the stack, even on a stack overflow. If it
- * fails, ignore the error. */
+#ifdef FAULTHANDLER_USE_ALT_STACK
+ memset(&stack, 0, sizeof(stack));
stack.ss_flags = 0;
/* bpo-21131: allocate dedicated stack of SIGSTKSZ*2 bytes, instead of just
SIGSTKSZ bytes. Calling the previous signal handler in faulthandler
signal handler uses more than SIGSTKSZ bytes of stack memory on some
platforms. */
stack.ss_size = SIGSTKSZ * 2;
- stack.ss_sp = PyMem_Malloc(stack.ss_size);
- if (stack.ss_sp != NULL) {
- err = sigaltstack(&stack, &old_stack);
- if (err) {
- PyMem_Free(stack.ss_sp);
- stack.ss_sp = NULL;
- }
- }
#endif
+
#ifdef FAULTHANDLER_LATER
- thread.file = NULL;
- thread.cancel_event = PyThread_allocate_lock();
- thread.running = PyThread_allocate_lock();
- if (!thread.cancel_event || !thread.running) {
- return _PyStatus_ERR("failed to allocate locks for faulthandler");
- }
- PyThread_acquire_lock(thread.cancel_event, 1);
+ memset(&thread, 0, sizeof(thread));
#endif
if (enable) {
/* fatal */
faulthandler_disable();
-#ifdef HAVE_SIGALTSTACK
+
+#ifdef FAULTHANDLER_USE_ALT_STACK
if (stack.ss_sp != NULL) {
/* Fetch the current alt stack */
stack_t current_stack;