summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2021-06-21 11:15:40 (GMT)
committerGitHub <noreply@github.com>2021-06-21 11:15:40 (GMT)
commitd19163912bfc790283724f05328bd31e4e65003d (patch)
treead8b66feb763211adee9f4b11d44c6c417266268
parentfb68791a26e157ed3cdeb409c8d8b6cddc7535bd (diff)
downloadcpython-d19163912bfc790283724f05328bd31e4e65003d.zip
cpython-d19163912bfc790283724f05328bd31e4e65003d.tar.gz
cpython-d19163912bfc790283724f05328bd31e4e65003d.tar.bz2
bpo-44466: Faulthandler now detects the GC (GH-26823)
The faulthandler module now detects if a fatal error occurs during a garbage collector collection (only if all_threads is true).
-rw-r--r--Doc/library/faulthandler.rst4
-rw-r--r--Doc/whatsnew/3.10.rst7
-rw-r--r--Lib/test/test_faulthandler.py67
-rw-r--r--Misc/NEWS.d/next/Library/2021-06-21-12-43-04.bpo-44466.NSm6mv.rst2
-rw-r--r--Python/traceback.c4
5 files changed, 70 insertions, 14 deletions
diff --git a/Doc/library/faulthandler.rst b/Doc/library/faulthandler.rst
index 59274c1..be09123 100644
--- a/Doc/library/faulthandler.rst
+++ b/Doc/library/faulthandler.rst
@@ -76,6 +76,10 @@ Fault handler state
.. versionchanged:: 3.6
On Windows, a handler for Windows exception is also installed.
+ .. versionchanged:: 3.10
+ The dump now mentions if a garbage collector collection is running
+ if *all_threads* is true.
+
.. function:: disable()
Disable the fault handler: uninstall the signal handlers installed by
diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst
index 528d8ab..5474062 100644
--- a/Doc/whatsnew/3.10.rst
+++ b/Doc/whatsnew/3.10.rst
@@ -1003,6 +1003,13 @@ Add *encoding* and *errors* parameters in :func:`fileinput.input` and
when *mode* is "r" and file is compressed, like uncompressed files.
(Contributed by Inada Naoki in :issue:`5758`.)
+faulthandler
+------------
+
+The :mod:`faulthandler` module now detects if a fatal error occurs during a
+garbage collector collection.
+(Contributed by Victor Stinner in :issue:`44466`.)
+
gc
--
diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py
index 29a7085..ee3f41a 100644
--- a/Lib/test/test_faulthandler.py
+++ b/Lib/test/test_faulthandler.py
@@ -89,10 +89,12 @@ class FaultHandlerTests(unittest.TestCase):
output = output.decode('ascii', 'backslashreplace')
return output.splitlines(), exitcode
- def check_error(self, code, line_number, fatal_error, *,
+ def check_error(self, code, lineno, fatal_error, *,
filename=None, all_threads=True, other_regex=None,
fd=None, know_current_thread=True,
- py_fatal_error=False):
+ py_fatal_error=False,
+ garbage_collecting=False,
+ function='<module>'):
"""
Check that the fault handler for fatal errors is enabled and check the
traceback from the child process output.
@@ -106,20 +108,21 @@ class FaultHandlerTests(unittest.TestCase):
header = 'Thread 0x[0-9a-f]+'
else:
header = 'Stack'
- regex = r"""
- (?m)^{fatal_error}
-
- {header} \(most recent call first\):
- File "<string>", line {lineno} in <module>
- """
+ regex = [f'^{fatal_error}']
if py_fatal_error:
- fatal_error += "\nPython runtime state: initialized"
- regex = dedent(regex).format(
- lineno=line_number,
- fatal_error=fatal_error,
- header=header).strip()
+ regex.append("Python runtime state: initialized")
+ regex.append('')
+ regex.append(fr'{header} \(most recent call first\):')
+ if garbage_collecting:
+ regex.append(' Garbage-collecting')
+ regex.append(fr' File "<string>", line {lineno} in {function}')
+ regex = '\n'.join(regex)
+
if other_regex:
- regex += '|' + other_regex
+ regex = f'(?:{regex}|{other_regex})'
+
+ # Enable MULTILINE flag
+ regex = f'(?m){regex}'
output, exitcode = self.get_output(code, filename=filename, fd=fd)
output = '\n'.join(output)
self.assertRegex(output, regex)
@@ -168,6 +171,42 @@ class FaultHandlerTests(unittest.TestCase):
3,
'Segmentation fault')
+ @skip_segfault_on_android
+ def test_gc(self):
+ # bpo-44466: Detect if the GC is running
+ self.check_fatal_error("""
+ import faulthandler
+ import gc
+ import sys
+
+ faulthandler.enable()
+
+ class RefCycle:
+ def __del__(self):
+ faulthandler._sigsegv()
+
+ # create a reference cycle which triggers a fatal
+ # error in a destructor
+ a = RefCycle()
+ b = RefCycle()
+ a.b = b
+ b.a = a
+
+ # Delete the objects, not the cycle
+ a = None
+ b = None
+
+ # Break the reference cycle: call __del__()
+ gc.collect()
+
+ # Should not reach this line
+ print("exit", file=sys.stderr)
+ """,
+ 9,
+ 'Segmentation fault',
+ function='__del__',
+ garbage_collecting=True)
+
def test_fatal_error_c_thread(self):
self.check_fatal_error("""
import faulthandler
diff --git a/Misc/NEWS.d/next/Library/2021-06-21-12-43-04.bpo-44466.NSm6mv.rst b/Misc/NEWS.d/next/Library/2021-06-21-12-43-04.bpo-44466.NSm6mv.rst
new file mode 100644
index 0000000..69de3ed
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-06-21-12-43-04.bpo-44466.NSm6mv.rst
@@ -0,0 +1,2 @@
+The :mod:`faulthandler` module now detects if a fatal error occurs during a
+garbage collector collection. Patch by Victor Stinner.
diff --git a/Python/traceback.c b/Python/traceback.c
index 470324b1..f7dc5ad 100644
--- a/Python/traceback.c
+++ b/Python/traceback.c
@@ -4,6 +4,7 @@
#include "Python.h"
#include "code.h"
+#include "pycore_interp.h" // PyInterpreterState.gc
#include "frameobject.h" // PyFrame_GetBack()
#include "structmember.h" // PyMemberDef
#include "osdefs.h" // SEP
@@ -914,6 +915,9 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
break;
}
write_thread_id(fd, tstate, tstate == current_tstate);
+ if (tstate == current_tstate && tstate->interp->gc.collecting) {
+ PUTS(fd, " Garbage-collecting\n");
+ }
dump_traceback(fd, tstate, 0);
tstate = PyThreadState_Next(tstate);
nthreads++;