mirror of
https://github.com/python/cpython.git
synced 2025-08-02 08:02:56 +00:00
bpo-38070: Py_FatalError() logs runtime state (GH-16246)
This commit is contained in:
parent
d3b904144e
commit
1ce16fb097
3 changed files with 73 additions and 32 deletions
|
@ -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):
|
||||||
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):
|
||||||
|
|
||||||
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):
|
||||||
{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):
|
||||||
""",
|
""",
|
||||||
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):
|
||||||
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):
|
||||||
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:
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
#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)
|
||||||
}
|
}
|
||||||
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", stream);
|
||||||
|
fflush(stream); /* it helps in Windows debug build */
|
||||||
|
|
||||||
|
_PyRuntimeState *runtime = &_PyRuntime;
|
||||||
|
fatal_error_dump_runtime(stream, runtime);
|
||||||
|
|
||||||
|
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
|
||||||
|
PyInterpreterState *interp = NULL;
|
||||||
|
if (tstate != NULL) {
|
||||||
|
interp = tstate->interp;
|
||||||
}
|
}
|
||||||
fputs("\n", stderr);
|
|
||||||
fflush(stderr); /* it helps in Windows debug build */
|
|
||||||
|
|
||||||
/* Check if the current thread has a Python thread state
|
/* Check if the current thread has a Python thread state
|
||||||
and holds the GIL */
|
and holds the GIL.
|
||||||
PyThreadState *tss_tstate = PyGILState_GetThisThreadState();
|
|
||||||
if (tss_tstate != NULL) {
|
|
||||||
PyThreadState *tstate = _PyThreadState_GET();
|
|
||||||
if (tss_tstate != tstate) {
|
|
||||||
/* The Python thread does not hold the GIL */
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue