Commit 404cdc5a authored by Victor Stinner's avatar Victor Stinner

faulthandler: add Windows exception handler

Issue #23848: On Windows, faulthandler.enable() now also installs an exception
handler to dump the traceback of all Python threads on any Windows exception,
not only on UNIX signals (SIGSEGV, SIGFPE, SIGABRT).
parent bd31b7c4
...@@ -68,6 +68,9 @@ Fault handler state ...@@ -68,6 +68,9 @@ Fault handler state
.. versionchanged:: 3.5 .. versionchanged:: 3.5
Added support for passing file descriptor to this function. Added support for passing file descriptor to this function.
.. versionchanged:: 3.6
On Windows, a handler for Windows exception is also installed.
.. function:: disable() .. function:: disable()
Disable the fault handler: uninstall the signal handlers installed by Disable the fault handler: uninstall the signal handlers installed by
......
...@@ -199,6 +199,14 @@ directives ``%G``, ``%u`` and ``%V``. ...@@ -199,6 +199,14 @@ directives ``%G``, ``%u`` and ``%V``.
(Contributed by Ashley Anderson in :issue:`12006`.) (Contributed by Ashley Anderson in :issue:`12006`.)
faulthandler
------------
On Windows, the :mod:`faulthandler` module now installs an handler for Windows
exceptions: see :func:`faulthandler.enable`. (Contributed by Victor Stinner in
:issue:`23848`.)
os os
-- --
......
...@@ -23,6 +23,7 @@ except ImportError: ...@@ -23,6 +23,7 @@ except ImportError:
_testcapi = None _testcapi = None
TIMEOUT = 0.5 TIMEOUT = 0.5
MS_WINDOWS = (os.name == 'nt')
def expected_traceback(lineno1, lineno2, header, min_count=1): def expected_traceback(lineno1, lineno2, header, min_count=1):
regex = header regex = header
...@@ -76,7 +77,7 @@ class FaultHandlerTests(unittest.TestCase): ...@@ -76,7 +77,7 @@ class FaultHandlerTests(unittest.TestCase):
output = output.decode('ascii', 'backslashreplace') output = output.decode('ascii', 'backslashreplace')
return output.splitlines(), exitcode return output.splitlines(), exitcode
def check_fatal_error(self, code, line_number, name_regex, 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):
""" """
...@@ -93,14 +94,14 @@ class FaultHandlerTests(unittest.TestCase): ...@@ -93,14 +94,14 @@ class FaultHandlerTests(unittest.TestCase):
else: else:
header = 'Stack' header = 'Stack'
regex = """ regex = """
^Fatal Python error: {name} ^{fatal_error}
{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( regex = dedent(regex.format(
lineno=line_number, lineno=line_number,
name=name_regex, fatal_error=fatal_error,
header=header)).strip() header=header)).strip()
if other_regex: if other_regex:
regex += '|' + other_regex regex += '|' + other_regex
...@@ -109,9 +110,18 @@ class FaultHandlerTests(unittest.TestCase): ...@@ -109,9 +110,18 @@ class FaultHandlerTests(unittest.TestCase):
self.assertRegex(output, regex) self.assertRegex(output, regex)
self.assertNotEqual(exitcode, 0) self.assertNotEqual(exitcode, 0)
def check_fatal_error(self, code, line_number, name_regex, **kw):
fatal_error = 'Fatal Python error: %s' % name_regex
self.check_error(code, line_number, fatal_error, **kw)
def check_windows_exception(self, code, line_number, name_regex, **kw):
fatal_error = 'Windows exception: %s' % name_regex
self.check_error(code, line_number, fatal_error, **kw)
@unittest.skipIf(sys.platform.startswith('aix'), @unittest.skipIf(sys.platform.startswith('aix'),
"the first page of memory is a mapped read-only on AIX") "the first page of memory is a mapped read-only on AIX")
def test_read_null(self): def test_read_null(self):
if not MS_WINDOWS:
self.check_fatal_error(""" self.check_fatal_error("""
import faulthandler import faulthandler
faulthandler.enable() faulthandler.enable()
...@@ -119,7 +129,17 @@ class FaultHandlerTests(unittest.TestCase): ...@@ -119,7 +129,17 @@ class FaultHandlerTests(unittest.TestCase):
""", """,
3, 3,
# Issue #12700: Read NULL raises SIGILL on Mac OS X Lion # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion
'(?:Segmentation fault|Bus error|Illegal instruction)') '(?:Segmentation fault'
'|Bus error'
'|Illegal instruction)')
else:
self.check_windows_exception("""
import faulthandler
faulthandler.enable()
faulthandler._read_null()
""",
3,
'access violation')
def test_sigsegv(self): def test_sigsegv(self):
self.check_fatal_error(""" self.check_fatal_error("""
...@@ -708,6 +728,22 @@ class FaultHandlerTests(unittest.TestCase): ...@@ -708,6 +728,22 @@ class FaultHandlerTests(unittest.TestCase):
with self.check_stderr_none(): with self.check_stderr_none():
faulthandler.register(signal.SIGUSR1) faulthandler.register(signal.SIGUSR1)
@unittest.skipUnless(MS_WINDOWS, 'specific to Windows')
def test_raise_exception(self):
for exc, name in (
('EXCEPTION_ACCESS_VIOLATION', 'access violation'),
('EXCEPTION_INT_DIVIDE_BY_ZERO', 'int divide by zero'),
('EXCEPTION_STACK_OVERFLOW', 'stack overflow'),
):
self.check_windows_exception(f"""
import faulthandler
faulthandler.enable()
faulthandler._raise_exception(faulthandler._{exc})
""",
3,
name)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
...@@ -232,6 +232,10 @@ Core and Builtins ...@@ -232,6 +232,10 @@ Core and Builtins
Library Library
------- -------
- Issue #23848: On Windows, faulthandler.enable() now also installs an
exception handler to dump the traceback of all Python threads on any Windows
exception, not only on UNIX signals (SIGSEGV, SIGFPE, SIGABRT).
- Issue #26530: Add C functions :c:func:`_PyTraceMalloc_Track` and - Issue #26530: Add C functions :c:func:`_PyTraceMalloc_Track` and
:c:func:`_PyTraceMalloc_Untrack` to track memory blocks using the :c:func:`_PyTraceMalloc_Untrack` to track memory blocks using the
:mod:`tracemalloc` module. Add :c:func:`_PyTraceMalloc_GetTraceback` to get :mod:`tracemalloc` module. Add :c:func:`_PyTraceMalloc_GetTraceback` to get
......
This diff is collapsed.
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