mirror of
https://github.com/python/cpython.git
synced 2025-07-24 11:44:31 +00:00
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).
This commit is contained in:
parent
fb68791a26
commit
d19163912b
5 changed files with 70 additions and 14 deletions
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue