diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2015-03-24 10:24:06 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2015-03-24 10:24:06 (GMT) |
commit | 0e98a76b65161b74a23f64e05a8075b46d908d10 (patch) | |
tree | 0a997ce4fe390468530efbbc88c8d9dafd21346c | |
parent | 2e3998fae0996bbb349a4696071f0582f8c799c0 (diff) | |
download | cpython-0e98a76b65161b74a23f64e05a8075b46d908d10.zip cpython-0e98a76b65161b74a23f64e05a8075b46d908d10.tar.gz cpython-0e98a76b65161b74a23f64e05a8075b46d908d10.tar.bz2 |
Issue #23571: Enhance Py_FatalError()
* Display the current Python stack if an exception was raised but the exception
has no traceback
* Disable faulthandler if an exception was raised (before it was only disabled
if no exception was raised)
* To display the current Python stack, call PyGILState_GetThisThreadState()
which works even if the GIL was released
-rw-r--r-- | Python/pythonrun.c | 73 |
1 files changed, 60 insertions, 13 deletions
diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 0327830..3217a9c 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -2587,28 +2587,75 @@ cleanup: } } +/* 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) +{ + PyObject *exception, *v, *tb; + int has_tb; + PyThreadState *tstate; + + PyErr_Fetch(&exception, &v, &tb); + if (exception == NULL) { + /* No current 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) { + /* too bad, PyErr_NormalizeException() failed */ + goto display_stack; + } + + has_tb = (tb != NULL && tb != Py_None); + PyErr_Display(exception, v, tb); + Py_XDECREF(exception); + Py_XDECREF(v); + Py_XDECREF(tb); + if (has_tb) + return; + +display_stack: + /* PyGILState_GetThisThreadState() works even if the GIL was released */ + tstate = PyGILState_GetThisThreadState(); + if (tstate == NULL) { + /* _Py_DumpTracebackThreads() requires the thread state to display + * frames */ + return; + } + + fputc('\n', stderr); + fflush(stderr); + + /* display the current Python stack */ + _Py_DumpTracebackThreads(fd, tstate->interp, tstate); +} + /* Print fatal error message and abort */ void Py_FatalError(const char *msg) { const int fd = fileno(stderr); - PyThreadState *tstate; fprintf(stderr, "Fatal Python error: %s\n", msg); fflush(stderr); /* it helps in Windows debug build */ - if (PyErr_Occurred()) { - PyErr_PrintEx(0); - } - else { - tstate = _Py_atomic_load_relaxed(&_PyThreadState_Current); - if (tstate != NULL) { - fputc('\n', stderr); - fflush(stderr); - _Py_DumpTracebackThreads(fd, tstate->interp, tstate); - } - _PyFaulthandler_Fini(); - } + + _Py_PrintFatalError(fd); + + /* The main purpose of faulthandler is to display the traceback. We already + * did our best to display it. So faulthandler can now be disabled. */ + _PyFaulthandler_Fini(); #ifdef MS_WINDOWS { |