Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The :mod:`faulthandler` module no longer allocates its alternative stack at
Python startup. Now the stack is only allocated at the first faulthandler
usage.
146 changes: 98 additions & 48 deletions Modules/faulthandler.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,13 @@ static fault_handler_t faulthandler_handlers[] = {
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
Expand Down Expand Up @@ -427,6 +433,36 @@ faulthandler_exc_handler(struct _EXCEPTION_POINTERS *exc_info)
}
#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
Expand All @@ -437,32 +473,35 @@ faulthandler_enable(void)
}
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) {
Expand Down Expand Up @@ -676,17 +715,37 @@ faulthandler_dump_traceback_later(PyObject *self,
}

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 */
Expand Down Expand Up @@ -728,9 +787,10 @@ faulthandler_cancel_dump_traceback_later_py(PyObject *self,
}
#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;
Expand All @@ -745,19 +805,19 @@ faulthandler_register(int signum, int chain, _Py_sighandler_t *p_previous)
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
}
Expand Down Expand Up @@ -861,6 +921,12 @@ faulthandler_register_py(PyObject *self,
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);
Expand Down Expand Up @@ -1094,7 +1160,7 @@ faulthandler_fatal_error_py(PyObject *self, PyObject *args)
Py_RETURN_NONE;
}

#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION)
#if defined(FAULTHANDLER_USE_ALT_STACK)
#define FAULTHANDLER_STACK_OVERFLOW

#ifdef __INTEL_COMPILER
Expand Down Expand Up @@ -1153,7 +1219,7 @@ faulthandler_stack_overflow(PyObject *self, PyObject *Py_UNUSED(ignored))
size, depth);
return NULL;
}
#endif /* defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) */
#endif /* defined(FAULTHANDLER_USE_ALT_STACK) && defined(HAVE_SIGACTION) */


static int
Expand Down Expand Up @@ -1318,35 +1384,18 @@ faulthandler_init_enable(void)
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) {
Expand Down Expand Up @@ -1386,7 +1435,8 @@ void _PyFaulthandler_Fini(void)

/* 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;
Expand Down