Commit 1ce16fb0 authored by Victor Stinner's avatar Victor Stinner Committed by GitHub

bpo-38070: Py_FatalError() logs runtime state (GH-16246)

parent d3b90414
...@@ -198,6 +198,7 @@ class CAPITest(unittest.TestCase): ...@@ -198,6 +198,7 @@ class CAPITest(unittest.TestCase):
self.assertRegex(err.replace(b'\r', b''), self.assertRegex(err.replace(b'\r', b''),
br'Fatal Python error: a function returned NULL ' br'Fatal Python error: a function returned NULL '
br'without setting an error\n' br'without setting an error\n'
br'Python runtime state: initialized\n'
br'SystemError: <built-in function ' br'SystemError: <built-in function '
br'return_null_without_error> returned NULL ' br'return_null_without_error> returned NULL '
br'without setting an error\n' br'without setting an error\n'
...@@ -225,6 +226,7 @@ class CAPITest(unittest.TestCase): ...@@ -225,6 +226,7 @@ class CAPITest(unittest.TestCase):
self.assertRegex(err.replace(b'\r', b''), self.assertRegex(err.replace(b'\r', b''),
br'Fatal Python error: a function returned a ' br'Fatal Python error: a function returned a '
br'result with an error set\n' br'result with an error set\n'
br'Python runtime state: initialized\n'
br'ValueError\n' br'ValueError\n'
br'\n' br'\n'
br'The above exception was the direct cause ' br'The above exception was the direct cause '
......
...@@ -90,7 +90,8 @@ class FaultHandlerTests(unittest.TestCase): ...@@ -90,7 +90,8 @@ class FaultHandlerTests(unittest.TestCase):
def check_error(self, code, line_number, fatal_error, *, def check_error(self, code, line_number, fatal_error, *,
filename=None, all_threads=True, other_regex=None, filename=None, all_threads=True, other_regex=None,
fd=None, know_current_thread=True): fd=None, know_current_thread=True,
py_fatal_error=False):
""" """
Check that the fault handler for fatal errors is enabled and check the Check that the fault handler for fatal errors is enabled and check the
traceback from the child process output. traceback from the child process output.
...@@ -110,10 +111,12 @@ class FaultHandlerTests(unittest.TestCase): ...@@ -110,10 +111,12 @@ class FaultHandlerTests(unittest.TestCase):
{header} \(most recent call first\): {header} \(most recent call first\):
File "<string>", line {lineno} in <module> File "<string>", line {lineno} in <module>
""" """
regex = dedent(regex.format( if py_fatal_error:
fatal_error += "\nPython runtime state: initialized"
regex = dedent(regex).format(
lineno=line_number, lineno=line_number,
fatal_error=fatal_error, fatal_error=fatal_error,
header=header)).strip() header=header).strip()
if other_regex: if other_regex:
regex += '|' + other_regex regex += '|' + other_regex
output, exitcode = self.get_output(code, filename=filename, fd=fd) output, exitcode = self.get_output(code, filename=filename, fd=fd)
...@@ -170,7 +173,8 @@ class FaultHandlerTests(unittest.TestCase): ...@@ -170,7 +173,8 @@ class FaultHandlerTests(unittest.TestCase):
""", """,
3, 3,
'in new thread', 'in new thread',
know_current_thread=False) know_current_thread=False,
py_fatal_error=True)
def test_sigabrt(self): def test_sigabrt(self):
self.check_fatal_error(""" self.check_fatal_error("""
...@@ -226,7 +230,8 @@ class FaultHandlerTests(unittest.TestCase): ...@@ -226,7 +230,8 @@ class FaultHandlerTests(unittest.TestCase):
faulthandler._fatal_error(b'xyz') faulthandler._fatal_error(b'xyz')
""", """,
2, 2,
'xyz') 'xyz',
py_fatal_error=True)
def test_fatal_error_without_gil(self): def test_fatal_error_without_gil(self):
self.check_fatal_error(""" self.check_fatal_error("""
...@@ -234,7 +239,8 @@ class FaultHandlerTests(unittest.TestCase): ...@@ -234,7 +239,8 @@ class FaultHandlerTests(unittest.TestCase):
faulthandler._fatal_error(b'xyz', True) faulthandler._fatal_error(b'xyz', True)
""", """,
2, 2,
'xyz') 'xyz',
py_fatal_error=True)
@unittest.skipIf(sys.platform.startswith('openbsd'), @unittest.skipIf(sys.platform.startswith('openbsd'),
"Issue #12868: sigaltstack() doesn't work on " "Issue #12868: sigaltstack() doesn't work on "
......
...@@ -1975,13 +1975,14 @@ done: ...@@ -1975,13 +1975,14 @@ done:
static void static void
_Py_FatalError_DumpTracebacks(int fd) _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp,
PyThreadState *tstate)
{ {
fputc('\n', stderr); fputc('\n', stderr);
fflush(stderr); fflush(stderr);
/* display the current Python stack */ /* display the current Python stack */
_Py_DumpTracebackThreads(fd, NULL, NULL); _Py_DumpTracebackThreads(fd, interp, tstate);
} }
/* Print the current exception (if an exception is set) with its traceback, /* Print the current exception (if an exception is set) with its traceback,
...@@ -2079,10 +2080,39 @@ fatal_output_debug(const char *msg) ...@@ -2079,10 +2080,39 @@ fatal_output_debug(const char *msg)
} }
#endif #endif
static void
fatal_error_dump_runtime(FILE *stream, _PyRuntimeState *runtime)
{
fprintf(stream, "Python runtime state: ");
if (runtime->finalizing) {
fprintf(stream, "finalizing (tstate=%p)", runtime->finalizing);
}
else if (runtime->initialized) {
fprintf(stream, "initialized");
}
else if (runtime->core_initialized) {
fprintf(stream, "core initialized");
}
else if (runtime->preinitialized) {
fprintf(stream, "preinitialized");
}
else if (runtime->preinitializing) {
fprintf(stream, "preinitializing");
}
else {
fprintf(stream, "unknown");
}
fprintf(stream, "\n");
fflush(stream);
}
static void _Py_NO_RETURN static void _Py_NO_RETURN
fatal_error(const char *prefix, const char *msg, int status) fatal_error(const char *prefix, const char *msg, int status)
{ {
const int fd = fileno(stderr); FILE *stream = stderr;
const int fd = fileno(stream);
static int reentrant = 0; static int reentrant = 0;
if (reentrant) { if (reentrant) {
...@@ -2092,45 +2122,48 @@ fatal_error(const char *prefix, const char *msg, int status) ...@@ -2092,45 +2122,48 @@ fatal_error(const char *prefix, const char *msg, int status)
} }
reentrant = 1; reentrant = 1;
fprintf(stderr, "Fatal Python error: "); fprintf(stream, "Fatal Python error: ");
if (prefix) { if (prefix) {
fputs(prefix, stderr); fputs(prefix, stream);
fputs(": ", stderr); fputs(": ", stream);
} }
if (msg) { if (msg) {
fputs(msg, stderr); fputs(msg, stream);
} }
else { else {
fprintf(stderr, "<message not set>"); fprintf(stream, "<message not set>");
} }
fputs("\n", stderr); fputs("\n", stream);
fflush(stderr); /* it helps in Windows debug build */ fflush(stream); /* it helps in Windows debug build */
/* Check if the current thread has a Python thread state _PyRuntimeState *runtime = &_PyRuntime;
and holds the GIL */ fatal_error_dump_runtime(stream, runtime);
PyThreadState *tss_tstate = PyGILState_GetThisThreadState();
if (tss_tstate != NULL) { PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
PyThreadState *tstate = _PyThreadState_GET(); PyInterpreterState *interp = NULL;
if (tss_tstate != tstate) { if (tstate != NULL) {
/* The Python thread does not hold the GIL */ interp = tstate->interp;
tss_tstate = NULL;
}
}
else {
/* Py_FatalError() has been called from a C thread
which has no Python thread state. */
} }
int has_tstate_and_gil = (tss_tstate != NULL);
/* Check if the current thread has a Python thread state
and holds the GIL.
tss_tstate is NULL if Py_FatalError() is called from a C thread which
has no Python thread state.
tss_tstate != tstate if the current Python thread does not hold the GIL.
*/
PyThreadState *tss_tstate = PyGILState_GetThisThreadState();
int has_tstate_and_gil = (tss_tstate != NULL && tss_tstate == tstate);
if (has_tstate_and_gil) { if (has_tstate_and_gil) {
/* If an exception is set, print the exception with its traceback */ /* If an exception is set, print the exception with its traceback */
if (!_Py_FatalError_PrintExc(fd)) { if (!_Py_FatalError_PrintExc(fd)) {
/* No exception is set, or an exception is set without traceback */ /* No exception is set, or an exception is set without traceback */
_Py_FatalError_DumpTracebacks(fd); _Py_FatalError_DumpTracebacks(fd, interp, tss_tstate);
} }
} }
else { else {
_Py_FatalError_DumpTracebacks(fd); _Py_FatalError_DumpTracebacks(fd, interp, tss_tstate);
} }
/* The main purpose of faulthandler is to display the traceback. /* The main purpose of faulthandler is to display the traceback.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment