Issue #23836: Fix the faulthandler module to handle reentrant calls
to its signal handlers. Use also _Py_write_noraise() instead of write() to retry write() if it is interrupted by a signal (fail with EINTR). faulthandler.dump_traceback() also calls PyErr_CheckSignals() to call the Python signal handler if a signal was received.
This commit is contained in:
parent
97f86b82b7
commit
c7489a5595
|
@ -13,6 +13,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #23836: Fix the faulthandler module to handle reentrant calls to
|
||||||
|
its signal handlers.
|
||||||
|
|
||||||
- Issue #23838: linecache now clears the cache and returns an empty result on
|
- Issue #23838: linecache now clears the cache and returns an empty result on
|
||||||
MemoryError.
|
MemoryError.
|
||||||
|
|
||||||
|
|
|
@ -28,9 +28,7 @@
|
||||||
# define FAULTHANDLER_USER
|
# define FAULTHANDLER_USER
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* cast size_t to int because write() takes an int on Windows
|
#define PUTS(fd, str) _Py_write_noraise(fd, str, strlen(str))
|
||||||
(anyway, the length is smaller than 30 characters) */
|
|
||||||
#define PUTS(fd, str) write(fd, str, (int)strlen(str))
|
|
||||||
|
|
||||||
_Py_IDENTIFIER(enable);
|
_Py_IDENTIFIER(enable);
|
||||||
_Py_IDENTIFIER(fileno);
|
_Py_IDENTIFIER(fileno);
|
||||||
|
@ -213,6 +211,42 @@ get_thread_state(void)
|
||||||
return tstate;
|
return tstate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
faulthandler_dump_traceback(int fd, int all_threads,
|
||||||
|
PyInterpreterState *interp)
|
||||||
|
{
|
||||||
|
static volatile int reentrant = 0;
|
||||||
|
PyThreadState *tstate;
|
||||||
|
|
||||||
|
if (reentrant)
|
||||||
|
return;
|
||||||
|
|
||||||
|
reentrant = 1;
|
||||||
|
|
||||||
|
#ifdef WITH_THREAD
|
||||||
|
/* SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL are synchronous signals and
|
||||||
|
are thus delivered to the thread that caused the fault. Get the Python
|
||||||
|
thread state of the current thread.
|
||||||
|
|
||||||
|
PyThreadState_Get() doesn't give the state of the thread that caused the
|
||||||
|
fault if the thread released the GIL, and so this function cannot be
|
||||||
|
used. Read the thread local storage (TLS) instead: call
|
||||||
|
PyGILState_GetThisThreadState(). */
|
||||||
|
tstate = PyGILState_GetThisThreadState();
|
||||||
|
#else
|
||||||
|
tstate = PyThreadState_Get();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (all_threads)
|
||||||
|
_Py_DumpTracebackThreads(fd, interp, tstate);
|
||||||
|
else {
|
||||||
|
if (tstate != NULL)
|
||||||
|
_Py_DumpTraceback(fd, tstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
reentrant = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
faulthandler_dump_traceback_py(PyObject *self,
|
faulthandler_dump_traceback_py(PyObject *self,
|
||||||
PyObject *args, PyObject *kwargs)
|
PyObject *args, PyObject *kwargs)
|
||||||
|
@ -247,6 +281,10 @@ faulthandler_dump_traceback_py(PyObject *self,
|
||||||
else {
|
else {
|
||||||
_Py_DumpTraceback(fd, tstate);
|
_Py_DumpTraceback(fd, tstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PyErr_CheckSignals())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +308,6 @@ faulthandler_fatal_error(int signum)
|
||||||
const int fd = fatal_error.fd;
|
const int fd = fatal_error.fd;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
fault_handler_t *handler = NULL;
|
fault_handler_t *handler = NULL;
|
||||||
PyThreadState *tstate;
|
|
||||||
int save_errno = errno;
|
int save_errno = errno;
|
||||||
|
|
||||||
if (!fatal_error.enabled)
|
if (!fatal_error.enabled)
|
||||||
|
@ -298,26 +335,8 @@ faulthandler_fatal_error(int signum)
|
||||||
PUTS(fd, handler->name);
|
PUTS(fd, handler->name);
|
||||||
PUTS(fd, "\n\n");
|
PUTS(fd, "\n\n");
|
||||||
|
|
||||||
#ifdef WITH_THREAD
|
faulthandler_dump_traceback(fd, fatal_error.all_threads,
|
||||||
/* SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL are synchronous signals and
|
fatal_error.interp);
|
||||||
are thus delivered to the thread that caused the fault. Get the Python
|
|
||||||
thread state of the current thread.
|
|
||||||
|
|
||||||
PyThreadState_Get() doesn't give the state of the thread that caused the
|
|
||||||
fault if the thread released the GIL, and so this function cannot be
|
|
||||||
used. Read the thread local storage (TLS) instead: call
|
|
||||||
PyGILState_GetThisThreadState(). */
|
|
||||||
tstate = PyGILState_GetThisThreadState();
|
|
||||||
#else
|
|
||||||
tstate = PyThreadState_Get();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (fatal_error.all_threads)
|
|
||||||
_Py_DumpTracebackThreads(fd, fatal_error.interp, tstate);
|
|
||||||
else {
|
|
||||||
if (tstate != NULL)
|
|
||||||
_Py_DumpTraceback(fd, tstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
errno = save_errno;
|
errno = save_errno;
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
|
@ -474,7 +493,7 @@ faulthandler_thread(void *unused)
|
||||||
/* get the thread holding the GIL, NULL if no thread hold the GIL */
|
/* get the thread holding the GIL, NULL if no thread hold the GIL */
|
||||||
current = (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current);
|
current = (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current);
|
||||||
|
|
||||||
write(thread.fd, thread.header, (int)thread.header_len);
|
_Py_write_noraise(thread.fd, thread.header, (int)thread.header_len);
|
||||||
|
|
||||||
errmsg = _Py_DumpTracebackThreads(thread.fd, thread.interp, current);
|
errmsg = _Py_DumpTracebackThreads(thread.fd, thread.interp, current);
|
||||||
ok = (errmsg == NULL);
|
ok = (errmsg == NULL);
|
||||||
|
@ -660,28 +679,14 @@ static void
|
||||||
faulthandler_user(int signum)
|
faulthandler_user(int signum)
|
||||||
{
|
{
|
||||||
user_signal_t *user;
|
user_signal_t *user;
|
||||||
PyThreadState *tstate;
|
|
||||||
int save_errno = errno;
|
int save_errno = errno;
|
||||||
|
|
||||||
user = &user_signals[signum];
|
user = &user_signals[signum];
|
||||||
if (!user->enabled)
|
if (!user->enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef WITH_THREAD
|
faulthandler_dump_traceback(user->fd, user->all_threads, user->interp);
|
||||||
/* PyThreadState_Get() doesn't give the state of the current thread if
|
|
||||||
the thread doesn't hold the GIL. Read the thread local storage (TLS)
|
|
||||||
instead: call PyGILState_GetThisThreadState(). */
|
|
||||||
tstate = PyGILState_GetThisThreadState();
|
|
||||||
#else
|
|
||||||
tstate = PyThreadState_Get();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (user->all_threads)
|
|
||||||
_Py_DumpTracebackThreads(user->fd, user->interp, tstate);
|
|
||||||
else {
|
|
||||||
if (tstate != NULL)
|
|
||||||
_Py_DumpTraceback(user->fd, tstate);
|
|
||||||
}
|
|
||||||
#ifdef HAVE_SIGACTION
|
#ifdef HAVE_SIGACTION
|
||||||
if (user->chain) {
|
if (user->chain) {
|
||||||
(void)sigaction(signum, &user->previous, NULL);
|
(void)sigaction(signum, &user->previous, NULL);
|
||||||
|
|
Loading…
Reference in New Issue