Skip to content

Isolate the faulthandler Module Between Interpreters #101509

Description

@ericsnowcurrently

(See gh-100227.)

Currently the faulthandler module has some state in _PyRuntimeState, including objects, which is shared by all interpreters. Interpreters should be isolated from each other, for a variety of reasons (including the possibility of a per-interpreter GIL). Isolating the module will involve moving some of the state to PyInterpreterState (and, under per-interpreter GIL, guarding other state with a global lock).


Code Analysis:

(expand)

Signals

The module installs handlers for various signals.

(expand)
fatal:
  • SIGBUS - bus error
  • SIGILL - illegal instruction
  • SIGFPE - floating point exception
  • SIGABRT - abort
  • SIGSEGV - segfault

"user":

  • the 5 fatal signals
  • others (up to Py_NSIG)

handlers:

  • faulthandler_fatal_error(signum)
  • faulthandler_exc_handler() (on Windows)
  • faulthandler_user()

State

Fields

https://github.com/python/cpython/blob/main/Include/internal/pycore_faulthandler.h#L49-L86
https://github.com/python/cpython/blob/main/Include/internal/pycore_runtime.h#L146

raw

struct_faulthandler_runtime_state {
struct {
intenabled;
PyObject*file;
intfd;
intall_threads;
PyInterpreterState*interp;
#ifdefMS_WINDOWS
void*exc_handler;
#endif
} fatal_error;
struct {
PyObject*file;
intfd;
PY_TIMEOUT_Ttimeout_us; /* timeout in microseconds */
intrepeat;
PyInterpreterState*interp;
intexit;
char*header;
size_theader_len;
/* The main thread always holds this lock. It is only released when
faulthandler_thread() is interrupted before this thread exits, or at
Python exit. */
PyThread_type_lockcancel_event;
/* released by child thread when joined */
PyThread_type_lockrunning;
} thread;
#ifdefFAULTHANDLER_USER
structfaulthandler_user_signal*user_signals;
#endif
#ifdefFAULTHANDLER_USE_ALT_STACK
stack_tstack;
stack_told_stack;
#endif
};

#ifdefFAULTHANDLER_USER
structfaulthandler_user_signal {
intenabled;
PyObject*file;
intfd;
intall_threads;
intchain;
_Py_sighandler_tprevious;
PyInterpreterState*interp;
};
#endif/* FAULTHANDLER_USER */

tables:

name

type

#ifdef

notes

fatal_error
. enabledbool"is faulthandler enabled?"
. filePyObject *only for keeping .fd alive
. fdint
. all_threadsbool
. interpPyInterpreterState *show only its threads
the one that enabled faulthandler
. exc_handlervoid *MS_WINDOWS

name

type

#ifdef

notes

thread---
. filePyObject *only for keeping .fd alive
. fdintdefaults to sys.stderr
. timeout_usPY_TIMEOUT_T
. repeatbool
. interpPyInterpreterState *show only its threads
the one that requested the info
. exitbool
. headerchar *
. header_lensize_t
. cancel_eventPyThread_type_lock
. runningPyThread_type_lock

name

type

#ifdef

notes

user_signalsstruct faulthandler_user_signal *FAULTHANDLER_USERa dynamically allocated array (Py_NSIG)
user_signals[signum]---
. enabledbool
. filePyObject *
. fdint
. all_threadsbool
. chainbool
. previous_Py_sighandler_t
. interpPyInterpreterState *show only its threads
the one that added the handler

name

type

#ifdef

notes

old_stackstack_tFAULTHANDLER_USE_ALT_STACK
stackstack_tFAULTHANDLER_USE_ALT_STACK

Usage

simple:

name

context

get

set

fatal_error
. enabledmodulefaulthandler_is_enabled()faulthandler_disable_py()
signalfaulthandler_fatal_error()
internalfaulthandler_enable()
faulthandler_disable()
faulthandler_enable()
faulthandler_disable()
. filemodulefaulthandler_traverse()faulthandler_py_enable()
faulthandler_traverse()
internalfaulthandler_disable()faulthandler_disable()
. fdmodule
signalfaulthandler_fatal_error()
faulthandler_exc_handler()
. all_threadsmodulefaulthandler_py_enable()
signalfaulthandler_fatal_error()
faulthandler_exc_handler()
. interpmodulefaulthandler_py_enable()
internalfaulthandler_fatal_error()
faulthandler_exc_handler()
. exc_handlerinternalfaulthandler_enable()
faulthandler_disable()
faulthandler_enable()
faulthandler_disable()

name

context

get

set

thread
. filemodulefaulthandler_dump_traceback_later()
faulthandler_traverse()
faulthandler_dump_traceback_later()
faulthandler_traverse()
internalcancel_dump_traceback_later()cancel_dump_traceback_later()
. fd
modulefaulthandler_dump_traceback_later()
threadfaulthandler_thread()
. timeout_usmodulefaulthandler_dump_traceback_later()
threadfaulthandler_thread()
. repeatmodulefaulthandler_dump_traceback_later()
threadfaulthandler_thread()
. interpmodulefaulthandler_dump_traceback_later()
threadfaulthandler_thread()
. exitmodulefaulthandler_dump_traceback_later()
threadfaulthandler_thread()
. headermodulefaulthandler_dump_traceback_later()
threadfaulthandler_thread()
internalcancel_dump_traceback_later()cancel_dump_traceback_later()
. header_lenmodulefaulthandler_dump_traceback_later()
threadfaulthandler_thread()
. cancel_eventmodulefaulthandler_dump_traceback_later()faulthandler_dump_traceback_later()
threadfaulthandler_thread()
internalcancel_dump_traceback_later()
. runningmodulefaulthandler_dump_traceback_later()faulthandler_dump_traceback_later()
threadfaulthandler_thread()
internalcancel_dump_traceback_later()

name

context

get

set

user_signalsmodulefaulthandler_register_py()
faulthandler_unregister_py()
faulthandler_register_py()
faulthandler_traverse()
C-API_PyFaulthandler_Fini()_PyFaulthandler_Fini()
signalfaulthandler_user()
user_signals[signum]
. enabledmodulefaulthandler_register_py()faulthandler_register_py()
signalfaulthandler_user()
internalfaulthandler_unregister()faulthandler_unregister()
. filemodulefaulthandler_register_py()
faulthandler_traverse()
faulthandler_register_py()
faulthandler_traverse()
internalfaulthandler_unregister()faulthandler_unregister()
. fdmodulefaulthandler_register_py()
signalfaulthandler_user()
internalfaulthandler_unregister()
. all_threadsmodulefaulthandler_register_py()
signalfaulthandler_user()
. chainmodulefaulthandler_register_py()
signalfaulthandler_user()
. previousmodulefaulthandler_register_py()
signalfaulthandler_user()
internalfaulthandler_unregister()
. interpmodulefaulthandler_register_py()
signalfaulthandler_user()

name

context

get

set

stackC-API_PyFaulthandler_Init()
_PyFaulthandler_Fini()
internalfaulthandler_enable()
faulthandler_register()
faulthandler_allocate_stack()
old_stackC-API_PyFaulthandler_Fini()
modify data in complex fields:

name

context

calls

fatal_error .fdsignalfaulthandler_fatal_error() -> faulthandler_dump_traceback()
faulthandler_fatal_error() -> _Py_DumpExtensionModules()
faulthandler_exc_handler()
faulthandler_exc_handler() -> _Py_DumpHexadecimal()
faulthandler_exc_handler() -> faulthandler_dump_traceback()
thread.fdthreadfaulthandler_thread() -> _Py_write_noraise()
faulthandler_thread() -> _Py_DumpTracebackThreads()
thread .cancel_eventmodulefaulthandler_dump_traceback_later()
C-API_PyFaulthandler_Fini()
threadfaulthandler_thread()
internalcancel_dump_traceback_later()
thread .runningmodulefaulthandler_dump_traceback_later()
threadfaulthandler_thread()
internalcancel_dump_traceback_later()
user_signals[signum] .fdsignalfaulthandler_user() -> faulthandler_dump_traceback()
stackC-API_PyFaulthandler_Init()
_PyFaulthandler_Fini()
internalfaulthandler_allocate_stack()
old_stackinternalfaulthandler_allocate_stack() -> sigaltstack()

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions