Fix Py_FatalError() if called without the GIL

Issue #26558: If Py_FatalError() is called without the GIL, don't try to print
the current exception, nor try to flush stdout and stderr: only dump the
traceback of Python threads.
This commit is contained in:
Victor Stinner 2016-03-14 16:53:12 +01:00
parent 34be807ca4
commit 791da1cc26
1 changed files with 70 additions and 54 deletions

View File

@ -1267,61 +1267,11 @@ initstdio(void)
}
/* Print the current exception (if an exception is set) with its traceback,
* or display the current Python stack.
*
* Don't call PyErr_PrintEx() and the except hook, because Py_FatalError() is
* called on catastrophic cases. */
static void
_Py_PrintFatalError(int fd)
_Py_FatalError_DumpTracebacks(int fd)
{
PyObject *ferr, *res;
PyObject *exception, *v, *tb;
int has_tb;
PyThreadState *tstate;
PyErr_Fetch(&exception, &v, &tb);
if (exception == NULL) {
/* No current exception */
goto display_stack;
}
ferr = _PySys_GetObjectId(&PyId_stderr);
if (ferr == NULL || ferr == Py_None) {
/* sys.stderr is not set yet or set to None,
no need to try to display the exception */
goto display_stack;
}
PyErr_NormalizeException(&exception, &v, &tb);
if (tb == NULL) {
tb = Py_None;
Py_INCREF(tb);
}
PyException_SetTraceback(v, tb);
if (exception == NULL) {
/* PyErr_NormalizeException() failed */
goto display_stack;
}
has_tb = (tb != Py_None);
PyErr_Display(exception, v, tb);
Py_XDECREF(exception);
Py_XDECREF(v);
Py_XDECREF(tb);
/* sys.stderr may be buffered: call sys.stderr.flush() */
res = _PyObject_CallMethodId(ferr, &PyId_flush, "");
if (res == NULL)
PyErr_Clear();
else
Py_DECREF(res);
if (has_tb)
return;
display_stack:
#ifdef WITH_THREAD
/* PyGILState_GetThisThreadState() works even if the GIL was released */
tstate = PyGILState_GetThisThreadState();
@ -1340,6 +1290,68 @@ display_stack:
/* display the current Python stack */
_Py_DumpTracebackThreads(fd, tstate->interp, tstate);
}
/* Print the current exception (if an exception is set) with its traceback,
or display the current Python stack.
Don't call PyErr_PrintEx() and the except hook, because Py_FatalError() is
called on catastrophic cases.
Return 1 if the traceback was displayed, 0 otherwise. */
static int
_Py_FatalError_PrintExc(int fd)
{
PyObject *ferr, *res;
PyObject *exception, *v, *tb;
int has_tb;
if (PyThreadState_GET() == NULL) {
/* The GIL is released: trying to acquire it is likely to deadlock,
just give up. */
return 0;
}
PyErr_Fetch(&exception, &v, &tb);
if (exception == NULL) {
/* No current exception */
return 0;
}
ferr = _PySys_GetObjectId(&PyId_stderr);
if (ferr == NULL || ferr == Py_None) {
/* sys.stderr is not set yet or set to None,
no need to try to display the exception */
return 0;
}
PyErr_NormalizeException(&exception, &v, &tb);
if (tb == NULL) {
tb = Py_None;
Py_INCREF(tb);
}
PyException_SetTraceback(v, tb);
if (exception == NULL) {
/* PyErr_NormalizeException() failed */
return 0;
}
has_tb = (tb != Py_None);
PyErr_Display(exception, v, tb);
Py_XDECREF(exception);
Py_XDECREF(v);
Py_XDECREF(tb);
/* sys.stderr may be buffered: call sys.stderr.flush() */
res = _PyObject_CallMethodId(ferr, &PyId_flush, "");
if (res == NULL)
PyErr_Clear();
else
Py_DECREF(res);
return has_tb;
}
/* Print fatal error message and abort */
void
@ -1365,10 +1377,14 @@ Py_FatalError(const char *msg)
/* Print the exception (if an exception is set) with its traceback,
* or display the current Python stack. */
_Py_PrintFatalError(fd);
if (!_Py_FatalError_PrintExc(fd))
_Py_FatalError_DumpTracebacks(fd);
/* Flush sys.stdout and sys.stderr */
flush_std_files();
/* Check if the current Python thread hold the GIL */
if (PyThreadState_GET() != NULL) {
/* Flush sys.stdout and sys.stderr */
flush_std_files();
}
/* The main purpose of faulthandler is to display the traceback. We already
* did our best to display it. So faulthandler can now be disabled.