| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | from contextlib import contextmanager | 
					
						
							| 
									
										
										
										
											2011-04-08 13:39:59 +02:00
										 |  |  | import datetime | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | import faulthandler | 
					
						
							| 
									
										
										
										
											2011-06-29 13:44:05 +02:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2021-01-18 20:47:13 +01:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | import signal | 
					
						
							|  |  |  | import subprocess | 
					
						
							|  |  |  | import sys | 
					
						
							| 
									
										
										
										
											2018-12-30 17:05:36 -08:00
										 |  |  | import sysconfig | 
					
						
							| 
									
										
										
										
											2015-05-06 06:33:17 +03:00
										 |  |  | from test import support | 
					
						
							| 
									
										
										
										
											2020-08-06 19:51:29 +08:00
										 |  |  | from test.support import os_helper | 
					
						
							| 
									
										
										
										
											2018-06-26 02:11:06 +02:00
										 |  |  | from test.support import script_helper, is_android | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | import tempfile | 
					
						
							|  |  |  | import unittest | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  | from textwrap import dedent | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-21 12:30:22 +02:00
										 |  |  | try: | 
					
						
							|  |  |  |     import _testcapi | 
					
						
							|  |  |  | except ImportError: | 
					
						
							|  |  |  |     _testcapi = None | 
					
						
							| 
									
										
										
										
											2011-04-07 11:50:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-04-01 15:37:12 +02:00
										 |  |  | TIMEOUT = 0.5 | 
					
						
							| 
									
										
										
										
											2018-06-26 02:11:06 +02:00
										 |  |  | MS_WINDOWS = (os.name == 'nt') | 
					
						
							| 
									
										
										
										
											2018-12-30 18:09:26 -08:00
										 |  |  | _cflags = sysconfig.get_config_var('CFLAGS') or '' | 
					
						
							|  |  |  | _config_args = sysconfig.get_config_var('CONFIG_ARGS') or '' | 
					
						
							|  |  |  | UB_SANITIZER = ( | 
					
						
							| 
									
										
										
										
											2018-12-30 20:15:41 -08:00
										 |  |  |     '-fsanitize=undefined' in _cflags or | 
					
						
							| 
									
										
										
										
											2018-12-30 18:09:26 -08:00
										 |  |  |     '--with-undefined-behavior-sanitizer' in _config_args | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2018-12-30 17:05:36 -08:00
										 |  |  | MEMORY_SANITIZER = ( | 
					
						
							| 
									
										
										
										
											2018-12-30 20:15:41 -08:00
										 |  |  |     '-fsanitize=memory' in _cflags or | 
					
						
							| 
									
										
										
										
											2018-12-30 18:09:26 -08:00
										 |  |  |     '--with-memory-sanitizer' in _config_args | 
					
						
							| 
									
										
										
										
											2018-12-30 17:05:36 -08:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2011-04-01 15:37:12 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-30 18:09:26 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-01 13:49:12 +02:00
										 |  |  | def expected_traceback(lineno1, lineno2, header, min_count=1): | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     regex = header | 
					
						
							| 
									
										
										
										
											2011-03-31 22:35:49 +02:00
										 |  |  |     regex += '  File "<string>", line %s in func\n' % lineno1 | 
					
						
							|  |  |  |     regex += '  File "<string>", line %s in <module>' % lineno2 | 
					
						
							| 
									
										
										
										
											2011-06-01 13:49:12 +02:00
										 |  |  |     if 1 < min_count: | 
					
						
							|  |  |  |         return '^' + (regex + '\n') * (min_count - 1) + regex | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return '^' + regex + '$' | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 11:43:23 +01:00
										 |  |  | def skip_segfault_on_android(test): | 
					
						
							|  |  |  |     # Issue #32138: Raising SIGSEGV on Android may not cause a crash. | 
					
						
							| 
									
										
										
										
											2018-06-26 02:11:06 +02:00
										 |  |  |     return unittest.skipIf(is_android, | 
					
						
							| 
									
										
										
										
											2017-11-29 11:43:23 +01:00
										 |  |  |                            'raising SIGSEGV on Android is unreliable')(test) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | @contextmanager | 
					
						
							|  |  |  | def temporary_filename(): | 
					
						
							|  |  |  |     filename = tempfile.mktemp() | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         yield filename | 
					
						
							|  |  |  |     finally: | 
					
						
							| 
									
										
										
										
											2020-08-06 19:51:29 +08:00
										 |  |  |         os_helper.unlink(filename) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | class FaultHandlerTests(unittest.TestCase): | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |     def get_output(self, code, filename=None, fd=None): | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Run the specified code in Python (in a new child process) and read the | 
					
						
							|  |  |  |         output from the standard error or from a file (if filename is set). | 
					
						
							|  |  |  |         Return the output lines as a list. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Strip the reference count from the standard error for Python debug | 
					
						
							|  |  |  |         build, and replace "Current thread 0x00007f8d8fbd9700" by "Current | 
					
						
							|  |  |  |         thread XXX". | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |         code = dedent(code).strip() | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |         pass_fds = [] | 
					
						
							|  |  |  |         if fd is not None: | 
					
						
							|  |  |  |             pass_fds.append(fd) | 
					
						
							| 
									
										
										
										
											2013-10-08 23:04:32 +02:00
										 |  |  |         with support.SuppressCrashReport(): | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             process = script_helper.spawn_python('-c', code, pass_fds=pass_fds) | 
					
						
							| 
									
										
										
										
											2016-03-16 22:45:24 +01:00
										 |  |  |             with process: | 
					
						
							| 
									
										
										
										
											2019-12-08 08:38:16 +01:00
										 |  |  |                 output, stderr = process.communicate() | 
					
						
							| 
									
										
										
										
											2016-03-16 22:45:24 +01:00
										 |  |  |                 exitcode = process.wait() | 
					
						
							| 
									
										
										
										
											2011-03-31 18:15:52 +02:00
										 |  |  |         output = output.decode('ascii', 'backslashreplace') | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         if filename: | 
					
						
							| 
									
										
										
										
											2011-03-31 18:15:52 +02:00
										 |  |  |             self.assertEqual(output, '') | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |             with open(filename, "rb") as fp: | 
					
						
							|  |  |  |                 output = fp.read() | 
					
						
							| 
									
										
										
										
											2011-03-31 18:15:52 +02:00
										 |  |  |             output = output.decode('ascii', 'backslashreplace') | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |         elif fd is not None: | 
					
						
							|  |  |  |             self.assertEqual(output, '') | 
					
						
							|  |  |  |             os.lseek(fd, os.SEEK_SET, 0) | 
					
						
							|  |  |  |             with open(fd, "rb", closefd=False) as fp: | 
					
						
							|  |  |  |                 output = fp.read() | 
					
						
							|  |  |  |             output = output.decode('ascii', 'backslashreplace') | 
					
						
							| 
									
										
										
										
											2011-03-31 13:29:56 +02:00
										 |  |  |         return output.splitlines(), exitcode | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-21 13:15:40 +02:00
										 |  |  |     def check_error(self, code, lineno, fatal_error, *, | 
					
						
							| 
									
										
										
										
											2016-03-23 10:39:17 +01:00
										 |  |  |                     filename=None, all_threads=True, other_regex=None, | 
					
						
							| 
									
										
										
										
											2019-09-18 01:35:33 +02:00
										 |  |  |                     fd=None, know_current_thread=True, | 
					
						
							| 
									
										
										
										
											2021-06-21 13:15:40 +02:00
										 |  |  |                     py_fatal_error=False, | 
					
						
							|  |  |  |                     garbage_collecting=False, | 
					
						
							|  |  |  |                     function='<module>'): | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Check that the fault handler for fatal errors is enabled and check the | 
					
						
							|  |  |  |         traceback from the child process output. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Raise an error if the output doesn't match the expected format. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if all_threads: | 
					
						
							| 
									
										
										
										
											2016-03-16 22:45:24 +01:00
										 |  |  |             if know_current_thread: | 
					
						
							|  |  |  |                 header = 'Current thread 0x[0-9a-f]+' | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 header = 'Thread 0x[0-9a-f]+' | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2016-03-16 22:45:24 +01:00
										 |  |  |             header = 'Stack' | 
					
						
							| 
									
										
										
										
											2021-06-21 13:15:40 +02:00
										 |  |  |         regex = [f'^{fatal_error}'] | 
					
						
							| 
									
										
										
										
											2019-09-18 01:35:33 +02:00
										 |  |  |         if py_fatal_error: | 
					
						
							| 
									
										
										
										
											2021-06-21 13:15:40 +02:00
										 |  |  |             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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-31 11:34:08 +02:00
										 |  |  |         if other_regex: | 
					
						
							| 
									
										
										
										
											2021-06-21 13:15:40 +02:00
										 |  |  |             regex = f'(?:{regex}|{other_regex})' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Enable MULTILINE flag | 
					
						
							|  |  |  |         regex = f'(?m){regex}' | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |         output, exitcode = self.get_output(code, filename=filename, fd=fd) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         output = '\n'.join(output) | 
					
						
							|  |  |  |         self.assertRegex(output, regex) | 
					
						
							| 
									
										
										
										
											2011-03-31 13:29:56 +02:00
										 |  |  |         self.assertNotEqual(exitcode, 0) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-07 00:54:20 +01:00
										 |  |  |     def check_fatal_error(self, code, line_number, name_regex, func=None, **kw): | 
					
						
							|  |  |  |         if func: | 
					
						
							|  |  |  |             name_regex = '%s: %s' % (func, name_regex) | 
					
						
							| 
									
										
										
										
											2016-03-23 10:39:17 +01:00
										 |  |  |         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): | 
					
						
							| 
									
										
										
										
											2016-03-23 14:44:14 +01:00
										 |  |  |         fatal_error = 'Windows fatal exception: %s' % name_regex | 
					
						
							| 
									
										
										
										
											2016-03-23 10:39:17 +01:00
										 |  |  |         self.check_error(code, line_number, fatal_error, **kw) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:29:42 +02:00
										 |  |  |     @unittest.skipIf(sys.platform.startswith('aix'), | 
					
						
							|  |  |  |                      "the first page of memory is a mapped read-only on AIX") | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     def test_read_null(self): | 
					
						
							| 
									
										
										
										
											2018-06-26 02:11:06 +02:00
										 |  |  |         if not MS_WINDOWS: | 
					
						
							| 
									
										
										
										
											2016-03-23 10:39:17 +01:00
										 |  |  |             self.check_fatal_error("""
 | 
					
						
							|  |  |  |                 import faulthandler | 
					
						
							|  |  |  |                 faulthandler.enable() | 
					
						
							|  |  |  |                 faulthandler._read_null() | 
					
						
							|  |  |  |                 """,
 | 
					
						
							|  |  |  |                 3, | 
					
						
							|  |  |  |                 # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion | 
					
						
							|  |  |  |                 '(?:Segmentation fault' | 
					
						
							|  |  |  |                     '|Bus error' | 
					
						
							|  |  |  |                     '|Illegal instruction)') | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.check_windows_exception("""
 | 
					
						
							|  |  |  |                 import faulthandler | 
					
						
							|  |  |  |                 faulthandler.enable() | 
					
						
							|  |  |  |                 faulthandler._read_null() | 
					
						
							|  |  |  |                 """,
 | 
					
						
							|  |  |  |                 3, | 
					
						
							|  |  |  |                 'access violation') | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 11:43:23 +01:00
										 |  |  |     @skip_segfault_on_android | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     def test_sigsegv(self): | 
					
						
							|  |  |  |         self.check_fatal_error("""
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             import faulthandler | 
					
						
							|  |  |  |             faulthandler.enable() | 
					
						
							|  |  |  |             faulthandler._sigsegv() | 
					
						
							|  |  |  |             """,
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |             3, | 
					
						
							|  |  |  |             'Segmentation fault') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-21 13:15:40 +02:00
										 |  |  |     @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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-16 22:45:24 +01:00
										 |  |  |     def test_fatal_error_c_thread(self): | 
					
						
							|  |  |  |         self.check_fatal_error("""
 | 
					
						
							|  |  |  |             import faulthandler | 
					
						
							|  |  |  |             faulthandler.enable() | 
					
						
							|  |  |  |             faulthandler._fatal_error_c_thread() | 
					
						
							|  |  |  |             """,
 | 
					
						
							|  |  |  |             3, | 
					
						
							|  |  |  |             'in new thread', | 
					
						
							| 
									
										
										
										
											2019-09-18 01:35:33 +02:00
										 |  |  |             know_current_thread=False, | 
					
						
							| 
									
										
										
										
											2020-03-07 00:54:20 +01:00
										 |  |  |             func='faulthandler_fatal_error_thread', | 
					
						
							| 
									
										
										
										
											2019-09-18 01:35:33 +02:00
										 |  |  |             py_fatal_error=True) | 
					
						
							| 
									
										
										
										
											2016-03-16 22:45:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-04-01 12:13:55 +02:00
										 |  |  |     def test_sigabrt(self): | 
					
						
							|  |  |  |         self.check_fatal_error("""
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             import faulthandler | 
					
						
							|  |  |  |             faulthandler.enable() | 
					
						
							|  |  |  |             faulthandler._sigabrt() | 
					
						
							|  |  |  |             """,
 | 
					
						
							| 
									
										
										
										
											2011-04-01 12:13:55 +02:00
										 |  |  |             3, | 
					
						
							|  |  |  |             'Aborted') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     @unittest.skipIf(sys.platform == 'win32', | 
					
						
							|  |  |  |                      "SIGFPE cannot be caught on Windows") | 
					
						
							|  |  |  |     def test_sigfpe(self): | 
					
						
							|  |  |  |         self.check_fatal_error("""
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             import faulthandler | 
					
						
							|  |  |  |             faulthandler.enable() | 
					
						
							|  |  |  |             faulthandler._sigfpe() | 
					
						
							|  |  |  |             """,
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |             3, | 
					
						
							|  |  |  |             'Floating point exception') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-21 12:30:22 +02:00
										 |  |  |     @unittest.skipIf(_testcapi is None, 'need _testcapi') | 
					
						
							|  |  |  |     @unittest.skipUnless(hasattr(signal, 'SIGBUS'), 'need signal.SIGBUS') | 
					
						
							| 
									
										
										
										
											2017-11-29 11:43:23 +01:00
										 |  |  |     @skip_segfault_on_android | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     def test_sigbus(self): | 
					
						
							|  |  |  |         self.check_fatal_error("""
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             import faulthandler | 
					
						
							| 
									
										
										
										
											2014-08-10 19:51:05 +02:00
										 |  |  |             import signal | 
					
						
							| 
									
										
										
										
											2014-07-21 12:30:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             faulthandler.enable() | 
					
						
							| 
									
										
										
										
											2019-01-08 01:58:25 -08:00
										 |  |  |             signal.raise_signal(signal.SIGBUS) | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             """,
 | 
					
						
							| 
									
										
										
										
											2019-01-08 01:58:25 -08:00
										 |  |  |             5, | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |             'Bus error') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-21 12:30:22 +02:00
										 |  |  |     @unittest.skipIf(_testcapi is None, 'need _testcapi') | 
					
						
							|  |  |  |     @unittest.skipUnless(hasattr(signal, 'SIGILL'), 'need signal.SIGILL') | 
					
						
							| 
									
										
										
										
											2017-11-29 11:43:23 +01:00
										 |  |  |     @skip_segfault_on_android | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     def test_sigill(self): | 
					
						
							|  |  |  |         self.check_fatal_error("""
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             import faulthandler | 
					
						
							| 
									
										
										
										
											2014-08-10 19:51:05 +02:00
										 |  |  |             import signal | 
					
						
							| 
									
										
										
										
											2014-07-21 12:30:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             faulthandler.enable() | 
					
						
							| 
									
										
										
										
											2019-01-08 01:58:25 -08:00
										 |  |  |             signal.raise_signal(signal.SIGILL) | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             """,
 | 
					
						
							| 
									
										
										
										
											2019-01-08 01:58:25 -08:00
										 |  |  |             5, | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |             'Illegal instruction') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-18 18:24:29 +01:00
										 |  |  |     def check_fatal_error_func(self, release_gil): | 
					
						
							|  |  |  |         # Test that Py_FatalError() dumps a traceback | 
					
						
							|  |  |  |         with support.SuppressCrashReport(): | 
					
						
							|  |  |  |             self.check_fatal_error(f"""
 | 
					
						
							|  |  |  |                 import _testcapi | 
					
						
							|  |  |  |                 _testcapi.fatal_error(b'xyz', {release_gil}) | 
					
						
							|  |  |  |                 """,
 | 
					
						
							|  |  |  |                 2, | 
					
						
							|  |  |  |                 'xyz', | 
					
						
							|  |  |  |                 func='test_fatal_error', | 
					
						
							|  |  |  |                 py_fatal_error=True) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     def test_fatal_error(self): | 
					
						
							| 
									
										
										
										
											2021-01-18 18:24:29 +01:00
										 |  |  |         self.check_fatal_error_func(False) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-15 17:23:35 +01:00
										 |  |  |     def test_fatal_error_without_gil(self): | 
					
						
							| 
									
										
										
										
											2021-01-18 18:24:29 +01:00
										 |  |  |         self.check_fatal_error_func(True) | 
					
						
							| 
									
										
										
										
											2016-03-15 17:23:35 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-07 18:56:24 +02:00
										 |  |  |     @unittest.skipIf(sys.platform.startswith('openbsd'), | 
					
						
							| 
									
										
										
										
											2011-09-01 23:08:21 +02:00
										 |  |  |                      "Issue #12868: sigaltstack() doesn't work on " | 
					
						
							|  |  |  |                      "OpenBSD if Python is compiled with pthread") | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'), | 
					
						
							|  |  |  |                      'need faulthandler._stack_overflow()') | 
					
						
							|  |  |  |     def test_stack_overflow(self): | 
					
						
							|  |  |  |         self.check_fatal_error("""
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             import faulthandler | 
					
						
							|  |  |  |             faulthandler.enable() | 
					
						
							|  |  |  |             faulthandler._stack_overflow() | 
					
						
							|  |  |  |             """,
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |             3, | 
					
						
							| 
									
										
										
										
											2011-03-31 11:34:08 +02:00
										 |  |  |             '(?:Segmentation fault|Bus error)', | 
					
						
							|  |  |  |             other_regex='unable to raise a stack overflow') | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 11:43:23 +01:00
										 |  |  |     @skip_segfault_on_android | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     def test_gil_released(self): | 
					
						
							|  |  |  |         self.check_fatal_error("""
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             import faulthandler | 
					
						
							|  |  |  |             faulthandler.enable() | 
					
						
							| 
									
										
										
										
											2014-09-30 13:54:14 +02:00
										 |  |  |             faulthandler._sigsegv(True) | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             """,
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |             3, | 
					
						
							| 
									
										
										
										
											2014-09-30 13:54:14 +02:00
										 |  |  |             'Segmentation fault') | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-30 18:09:26 -08:00
										 |  |  |     @unittest.skipIf(UB_SANITIZER or MEMORY_SANITIZER, | 
					
						
							| 
									
										
										
										
											2018-12-30 20:15:41 -08:00
										 |  |  |                      "sanitizer builds change crashing process output.") | 
					
						
							| 
									
										
										
										
											2017-11-29 11:43:23 +01:00
										 |  |  |     @skip_segfault_on_android | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     def test_enable_file(self): | 
					
						
							|  |  |  |         with temporary_filename() as filename: | 
					
						
							|  |  |  |             self.check_fatal_error("""
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |                 import faulthandler | 
					
						
							|  |  |  |                 output = open({filename}, 'wb') | 
					
						
							|  |  |  |                 faulthandler.enable(output) | 
					
						
							|  |  |  |                 faulthandler._sigsegv() | 
					
						
							|  |  |  |                 """.format(filename=repr(filename)),
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |                 4, | 
					
						
							| 
									
										
										
										
											2013-06-17 23:37:59 +02:00
										 |  |  |                 'Segmentation fault', | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |                 filename=filename) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-13 11:01:30 +01:00
										 |  |  |     @unittest.skipIf(sys.platform == "win32", | 
					
						
							|  |  |  |                      "subprocess doesn't support pass_fds on Windows") | 
					
						
							| 
									
										
										
										
											2018-12-30 18:09:26 -08:00
										 |  |  |     @unittest.skipIf(UB_SANITIZER or MEMORY_SANITIZER, | 
					
						
							| 
									
										
										
										
											2018-12-30 20:15:41 -08:00
										 |  |  |                      "sanitizer builds change crashing process output.") | 
					
						
							| 
									
										
										
										
											2017-11-29 11:43:23 +01:00
										 |  |  |     @skip_segfault_on_android | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |     def test_enable_fd(self): | 
					
						
							|  |  |  |         with tempfile.TemporaryFile('wb+') as fp: | 
					
						
							|  |  |  |             fd = fp.fileno() | 
					
						
							|  |  |  |             self.check_fatal_error("""
 | 
					
						
							|  |  |  |                 import faulthandler | 
					
						
							|  |  |  |                 import sys | 
					
						
							|  |  |  |                 faulthandler.enable(%s) | 
					
						
							|  |  |  |                 faulthandler._sigsegv() | 
					
						
							|  |  |  |                 """ % fd,
 | 
					
						
							|  |  |  |                 4, | 
					
						
							|  |  |  |                 'Segmentation fault', | 
					
						
							|  |  |  |                 fd=fd) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 11:43:23 +01:00
										 |  |  |     @skip_segfault_on_android | 
					
						
							| 
									
										
										
										
											2011-05-07 12:43:00 +02:00
										 |  |  |     def test_enable_single_thread(self): | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         self.check_fatal_error("""
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             import faulthandler | 
					
						
							|  |  |  |             faulthandler.enable(all_threads=False) | 
					
						
							|  |  |  |             faulthandler._sigsegv() | 
					
						
							|  |  |  |             """,
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |             3, | 
					
						
							| 
									
										
										
										
											2013-06-17 23:37:59 +02:00
										 |  |  |             'Segmentation fault', | 
					
						
							| 
									
										
										
										
											2011-05-07 12:43:00 +02:00
										 |  |  |             all_threads=False) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 11:43:23 +01:00
										 |  |  |     @skip_segfault_on_android | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     def test_disable(self): | 
					
						
							|  |  |  |         code = """
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             import faulthandler | 
					
						
							|  |  |  |             faulthandler.enable() | 
					
						
							|  |  |  |             faulthandler.disable() | 
					
						
							|  |  |  |             faulthandler._sigsegv() | 
					
						
							|  |  |  |             """
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         not_expected = 'Fatal Python error' | 
					
						
							| 
									
										
										
										
											2013-10-08 23:04:32 +02:00
										 |  |  |         stderr, exitcode = self.get_output(code) | 
					
						
							| 
									
										
										
										
											2014-09-25 00:38:48 +02:00
										 |  |  |         stderr = '\n'.join(stderr) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         self.assertTrue(not_expected not in stderr, | 
					
						
							|  |  |  |                      "%r is present in %r" % (not_expected, stderr)) | 
					
						
							| 
									
										
										
										
											2011-03-31 13:29:56 +02:00
										 |  |  |         self.assertNotEqual(exitcode, 0) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-18 20:47:13 +01:00
										 |  |  |     @skip_segfault_on_android | 
					
						
							|  |  |  |     def test_dump_ext_modules(self): | 
					
						
							|  |  |  |         code = """
 | 
					
						
							|  |  |  |             import faulthandler | 
					
						
							| 
									
										
										
										
											2021-01-25 13:24:42 +01:00
										 |  |  |             import sys | 
					
						
							|  |  |  |             # Don't filter stdlib module names | 
					
						
							| 
									
										
										
										
											2021-01-25 23:12:50 +01:00
										 |  |  |             sys.stdlib_module_names = frozenset() | 
					
						
							| 
									
										
										
										
											2021-01-18 20:47:13 +01:00
										 |  |  |             faulthandler.enable() | 
					
						
							|  |  |  |             faulthandler._sigsegv() | 
					
						
							|  |  |  |             """
 | 
					
						
							|  |  |  |         stderr, exitcode = self.get_output(code) | 
					
						
							|  |  |  |         stderr = '\n'.join(stderr) | 
					
						
							| 
									
										
										
										
											2021-01-19 23:35:27 +01:00
										 |  |  |         match = re.search(r'^Extension modules:(.*) \(total: [0-9]+\)$', | 
					
						
							|  |  |  |                           stderr, re.MULTILINE) | 
					
						
							| 
									
										
										
										
											2021-01-18 20:47:13 +01:00
										 |  |  |         if not match: | 
					
						
							|  |  |  |             self.fail(f"Cannot find 'Extension modules:' in {stderr!r}") | 
					
						
							|  |  |  |         modules = set(match.group(1).strip().split(', ')) | 
					
						
							| 
									
										
										
										
											2021-01-25 13:24:42 +01:00
										 |  |  |         for name in ('sys', 'faulthandler'): | 
					
						
							|  |  |  |             self.assertIn(name, modules) | 
					
						
							| 
									
										
										
										
											2021-01-18 20:47:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     def test_is_enabled(self): | 
					
						
							| 
									
										
										
										
											2011-06-29 13:44:05 +02:00
										 |  |  |         orig_stderr = sys.stderr | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2011-06-29 13:44:05 +02:00
										 |  |  |             # regrtest may replace sys.stderr by io.StringIO object, but | 
					
						
							|  |  |  |             # faulthandler.enable() requires that sys.stderr has a fileno() | 
					
						
							|  |  |  |             # method | 
					
						
							| 
									
										
										
										
											2011-06-29 23:24:31 +02:00
										 |  |  |             sys.stderr = sys.__stderr__ | 
					
						
							| 
									
										
										
										
											2011-06-29 13:44:05 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             was_enabled = faulthandler.is_enabled() | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |                 faulthandler.enable() | 
					
						
							| 
									
										
										
										
											2011-06-29 13:44:05 +02:00
										 |  |  |                 self.assertTrue(faulthandler.is_enabled()) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |                 faulthandler.disable() | 
					
						
							| 
									
										
										
										
											2011-06-29 13:44:05 +02:00
										 |  |  |                 self.assertFalse(faulthandler.is_enabled()) | 
					
						
							|  |  |  |             finally: | 
					
						
							|  |  |  |                 if was_enabled: | 
					
						
							|  |  |  |                     faulthandler.enable() | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     faulthandler.disable() | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             sys.stderr = orig_stderr | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-31 02:55:49 +02:00
										 |  |  |     def test_disabled_by_default(self): | 
					
						
							|  |  |  |         # By default, the module should be disabled | 
					
						
							|  |  |  |         code = "import faulthandler; print(faulthandler.is_enabled())" | 
					
						
							| 
									
										
										
										
											2017-12-12 23:15:00 +01:00
										 |  |  |         args = (sys.executable, "-E", "-c", code) | 
					
						
							| 
									
										
										
										
											2015-01-22 17:33:28 -08:00
										 |  |  |         # don't use assert_python_ok() because it always enables faulthandler | 
					
						
							| 
									
										
										
										
											2017-12-12 23:15:00 +01:00
										 |  |  |         output = subprocess.check_output(args) | 
					
						
							| 
									
										
										
										
											2013-09-08 11:36:23 +02:00
										 |  |  |         self.assertEqual(output.rstrip(), b"False") | 
					
						
							| 
									
										
										
										
											2012-07-31 02:55:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_sys_xoptions(self): | 
					
						
							|  |  |  |         # Test python -X faulthandler | 
					
						
							|  |  |  |         code = "import faulthandler; print(faulthandler.is_enabled())" | 
					
						
							| 
									
										
										
										
											2015-01-22 17:33:28 -08:00
										 |  |  |         args = filter(None, (sys.executable, | 
					
						
							|  |  |  |                              "-E" if sys.flags.ignore_environment else "", | 
					
						
							|  |  |  |                              "-X", "faulthandler", "-c", code)) | 
					
						
							|  |  |  |         env = os.environ.copy() | 
					
						
							|  |  |  |         env.pop("PYTHONFAULTHANDLER", None) | 
					
						
							|  |  |  |         # don't use assert_python_ok() because it always enables faulthandler | 
					
						
							|  |  |  |         output = subprocess.check_output(args, env=env) | 
					
						
							| 
									
										
										
										
											2013-09-08 11:36:23 +02:00
										 |  |  |         self.assertEqual(output.rstrip(), b"True") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_env_var(self): | 
					
						
							|  |  |  |         # empty env var | 
					
						
							|  |  |  |         code = "import faulthandler; print(faulthandler.is_enabled())" | 
					
						
							|  |  |  |         args = (sys.executable, "-c", code) | 
					
						
							| 
									
										
										
										
											2017-12-12 23:15:00 +01:00
										 |  |  |         env = dict(os.environ) | 
					
						
							| 
									
										
										
										
											2013-09-08 11:36:23 +02:00
										 |  |  |         env['PYTHONFAULTHANDLER'] = '' | 
					
						
							| 
									
										
										
										
											2017-12-12 23:15:00 +01:00
										 |  |  |         env['PYTHONDEVMODE'] = '' | 
					
						
							| 
									
										
										
										
											2015-01-22 17:33:28 -08:00
										 |  |  |         # don't use assert_python_ok() because it always enables faulthandler | 
					
						
							| 
									
										
										
										
											2013-09-08 11:36:23 +02:00
										 |  |  |         output = subprocess.check_output(args, env=env) | 
					
						
							|  |  |  |         self.assertEqual(output.rstrip(), b"False") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # non-empty env var | 
					
						
							| 
									
										
										
										
											2017-12-12 23:15:00 +01:00
										 |  |  |         env = dict(os.environ) | 
					
						
							| 
									
										
										
										
											2013-09-08 11:36:23 +02:00
										 |  |  |         env['PYTHONFAULTHANDLER'] = '1' | 
					
						
							| 
									
										
										
										
											2017-12-12 23:15:00 +01:00
										 |  |  |         env['PYTHONDEVMODE'] = '' | 
					
						
							| 
									
										
										
										
											2013-09-08 11:36:23 +02:00
										 |  |  |         output = subprocess.check_output(args, env=env) | 
					
						
							|  |  |  |         self.assertEqual(output.rstrip(), b"True") | 
					
						
							| 
									
										
										
										
											2012-07-31 02:55:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |     def check_dump_traceback(self, *, filename=None, fd=None): | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Explicitly call dump_traceback() function and check its output. | 
					
						
							|  |  |  |         Raise an error if the output doesn't match the expected format. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         code = """
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             import faulthandler | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             filename = {filename!r} | 
					
						
							|  |  |  |             fd = {fd} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             def funcB(): | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |                 if filename: | 
					
						
							|  |  |  |                     with open(filename, "wb") as fp: | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |                         faulthandler.dump_traceback(fp, all_threads=False) | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |                 elif fd is not None: | 
					
						
							|  |  |  |                     faulthandler.dump_traceback(fd, | 
					
						
							|  |  |  |                                                 all_threads=False) | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |                 else: | 
					
						
							|  |  |  |                     faulthandler.dump_traceback(all_threads=False) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             def funcA(): | 
					
						
							|  |  |  |                 funcB() | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             funcA() | 
					
						
							|  |  |  |             """
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         code = code.format( | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             filename=filename, | 
					
						
							|  |  |  |             fd=fd, | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         ) | 
					
						
							|  |  |  |         if filename: | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             lineno = 9 | 
					
						
							|  |  |  |         elif fd is not None: | 
					
						
							| 
									
										
										
										
											2018-09-17 15:17:29 +03:00
										 |  |  |             lineno = 11 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             lineno = 14 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         expected = [ | 
					
						
							| 
									
										
										
										
											2013-10-20 18:21:02 -07:00
										 |  |  |             'Stack (most recent call first):', | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |             '  File "<string>", line %s in funcB' % lineno, | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             '  File "<string>", line 17 in funcA', | 
					
						
							|  |  |  |             '  File "<string>", line 19 in <module>' | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         ] | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |         trace, exitcode = self.get_output(code, filename, fd) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         self.assertEqual(trace, expected) | 
					
						
							| 
									
										
										
										
											2011-03-31 13:29:56 +02:00
										 |  |  |         self.assertEqual(exitcode, 0) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_dump_traceback(self): | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |         self.check_dump_traceback() | 
					
						
							| 
									
										
										
										
											2011-03-31 18:15:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_dump_traceback_file(self): | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         with temporary_filename() as filename: | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             self.check_dump_traceback(filename=filename) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-13 11:01:30 +01:00
										 |  |  |     @unittest.skipIf(sys.platform == "win32", | 
					
						
							|  |  |  |                      "subprocess doesn't support pass_fds on Windows") | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |     def test_dump_traceback_fd(self): | 
					
						
							|  |  |  |         with tempfile.TemporaryFile('wb+') as fp: | 
					
						
							|  |  |  |             self.check_dump_traceback(fd=fp.fileno()) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-01 19:45:34 +02:00
										 |  |  |     def test_truncate(self): | 
					
						
							|  |  |  |         maxlen = 500 | 
					
						
							|  |  |  |         func_name = 'x' * (maxlen + 50) | 
					
						
							|  |  |  |         truncated = 'x' * maxlen + '...' | 
					
						
							|  |  |  |         code = """
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             import faulthandler | 
					
						
							| 
									
										
										
										
											2012-08-01 19:45:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             def {func_name}(): | 
					
						
							|  |  |  |                 faulthandler.dump_traceback(all_threads=False) | 
					
						
							| 
									
										
										
										
											2012-08-01 19:45:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             {func_name}() | 
					
						
							|  |  |  |             """
 | 
					
						
							| 
									
										
										
										
											2012-08-01 19:45:34 +02:00
										 |  |  |         code = code.format( | 
					
						
							|  |  |  |             func_name=func_name, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         expected = [ | 
					
						
							| 
									
										
										
										
											2013-10-20 18:21:02 -07:00
										 |  |  |             'Stack (most recent call first):', | 
					
						
							| 
									
										
										
										
											2012-08-01 19:45:34 +02:00
										 |  |  |             '  File "<string>", line 4 in %s' % truncated, | 
					
						
							|  |  |  |             '  File "<string>", line 6 in <module>' | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |         trace, exitcode = self.get_output(code) | 
					
						
							|  |  |  |         self.assertEqual(trace, expected) | 
					
						
							|  |  |  |         self.assertEqual(exitcode, 0) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     def check_dump_traceback_threads(self, filename): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Call explicitly dump_traceback(all_threads=True) and check the output. | 
					
						
							|  |  |  |         Raise an error if the output doesn't match the expected format. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         code = """
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             import faulthandler | 
					
						
							|  |  |  |             from threading import Thread, Event | 
					
						
							|  |  |  |             import time | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             def dump(): | 
					
						
							|  |  |  |                 if {filename}: | 
					
						
							|  |  |  |                     with open({filename}, "wb") as fp: | 
					
						
							|  |  |  |                         faulthandler.dump_traceback(fp, all_threads=True) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     faulthandler.dump_traceback(all_threads=True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             class Waiter(Thread): | 
					
						
							|  |  |  |                 # avoid blocking if the main thread raises an exception. | 
					
						
							|  |  |  |                 daemon = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 def __init__(self): | 
					
						
							|  |  |  |                     Thread.__init__(self) | 
					
						
							|  |  |  |                     self.running = Event() | 
					
						
							|  |  |  |                     self.stop = Event() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 def run(self): | 
					
						
							|  |  |  |                     self.running.set() | 
					
						
							|  |  |  |                     self.stop.wait() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             waiter = Waiter() | 
					
						
							|  |  |  |             waiter.start() | 
					
						
							|  |  |  |             waiter.running.wait() | 
					
						
							|  |  |  |             dump() | 
					
						
							|  |  |  |             waiter.stop.set() | 
					
						
							|  |  |  |             waiter.join() | 
					
						
							|  |  |  |             """
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         code = code.format(filename=repr(filename)) | 
					
						
							| 
									
										
										
										
											2011-03-31 13:29:56 +02:00
										 |  |  |         output, exitcode = self.get_output(code, filename) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         output = '\n'.join(output) | 
					
						
							|  |  |  |         if filename: | 
					
						
							|  |  |  |             lineno = 8 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             lineno = 10 | 
					
						
							| 
									
										
										
										
											2016-09-08 13:59:53 -04:00
										 |  |  |         regex = r"""
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             ^Thread 0x[0-9a-f]+ \(most recent call first\): | 
					
						
							|  |  |  |             (?:  File ".*threading.py", line [0-9]+ in [_a-z]+ | 
					
						
							|  |  |  |             ){{1,3}}  File "<string>", line 23 in run | 
					
						
							|  |  |  |               File ".*threading.py", line [0-9]+ in _bootstrap_inner | 
					
						
							|  |  |  |               File ".*threading.py", line [0-9]+ in _bootstrap | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-16 22:45:24 +01:00
										 |  |  |             Current thread 0x[0-9a-f]+ \(most recent call first\): | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |               File "<string>", line {lineno} in dump | 
					
						
							|  |  |  |               File "<string>", line 28 in <module>$ | 
					
						
							|  |  |  |             """
 | 
					
						
							|  |  |  |         regex = dedent(regex.format(lineno=lineno)).strip() | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         self.assertRegex(output, regex) | 
					
						
							| 
									
										
										
										
											2011-03-31 13:29:56 +02:00
										 |  |  |         self.assertEqual(exitcode, 0) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_dump_traceback_threads(self): | 
					
						
							|  |  |  |         self.check_dump_traceback_threads(None) | 
					
						
							| 
									
										
										
										
											2011-03-31 18:15:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_dump_traceback_threads_file(self): | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         with temporary_filename() as filename: | 
					
						
							|  |  |  |             self.check_dump_traceback_threads(filename) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |     def check_dump_traceback_later(self, repeat=False, cancel=False, loops=1, | 
					
						
							|  |  |  |                                    *, filename=None, fd=None): | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Check how many times the traceback is written in timeout x 2.5 seconds, | 
					
						
							|  |  |  |         or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending | 
					
						
							|  |  |  |         on repeat and cancel options. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Raise an error if the output doesn't match the expect format. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2011-04-08 13:39:59 +02:00
										 |  |  |         timeout_str = str(datetime.timedelta(seconds=TIMEOUT)) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         code = """
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             import faulthandler | 
					
						
							|  |  |  |             import time | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             import sys | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             timeout = {timeout} | 
					
						
							|  |  |  |             repeat = {repeat} | 
					
						
							|  |  |  |             cancel = {cancel} | 
					
						
							|  |  |  |             loops = {loops} | 
					
						
							|  |  |  |             filename = {filename!r} | 
					
						
							|  |  |  |             fd = {fd} | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             def func(timeout, repeat, cancel, file, loops): | 
					
						
							|  |  |  |                 for loop in range(loops): | 
					
						
							|  |  |  |                     faulthandler.dump_traceback_later(timeout, repeat=repeat, file=file) | 
					
						
							|  |  |  |                     if cancel: | 
					
						
							|  |  |  |                         faulthandler.cancel_dump_traceback_later() | 
					
						
							|  |  |  |                     time.sleep(timeout * 5) | 
					
						
							|  |  |  |                     faulthandler.cancel_dump_traceback_later() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             if filename: | 
					
						
							|  |  |  |                 file = open(filename, "wb") | 
					
						
							|  |  |  |             elif fd is not None: | 
					
						
							|  |  |  |                 file = sys.stderr.fileno() | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 file = None | 
					
						
							|  |  |  |             func(timeout, repeat, cancel, file, loops) | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             if filename: | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |                 file.close() | 
					
						
							|  |  |  |             """
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         code = code.format( | 
					
						
							| 
									
										
										
										
											2011-04-08 12:57:06 +02:00
										 |  |  |             timeout=TIMEOUT, | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |             repeat=repeat, | 
					
						
							|  |  |  |             cancel=cancel, | 
					
						
							| 
									
										
										
										
											2011-04-08 12:57:06 +02:00
										 |  |  |             loops=loops, | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             filename=filename, | 
					
						
							|  |  |  |             fd=fd, | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2011-03-31 13:29:56 +02:00
										 |  |  |         trace, exitcode = self.get_output(code, filename) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         trace = '\n'.join(trace) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-04-03 18:45:42 +02:00
										 |  |  |         if not cancel: | 
					
						
							| 
									
										
										
										
											2011-04-08 12:57:06 +02:00
										 |  |  |             count = loops | 
					
						
							| 
									
										
										
										
											2011-04-03 18:45:42 +02:00
										 |  |  |             if repeat: | 
					
						
							| 
									
										
										
										
											2011-04-08 12:57:06 +02:00
										 |  |  |                 count *= 2 | 
					
						
							| 
									
										
										
										
											2013-10-20 18:21:02 -07:00
										 |  |  |             header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+ \(most recent call first\):\n' % timeout_str | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             regex = expected_traceback(17, 26, header, min_count=count) | 
					
						
							| 
									
										
										
										
											2011-04-03 18:45:42 +02:00
										 |  |  |             self.assertRegex(trace, regex) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2011-04-03 18:45:42 +02:00
										 |  |  |             self.assertEqual(trace, '') | 
					
						
							| 
									
										
										
										
											2011-03-31 13:29:56 +02:00
										 |  |  |         self.assertEqual(exitcode, 0) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-22 08:58:55 +02:00
										 |  |  |     def test_dump_traceback_later(self): | 
					
						
							|  |  |  |         self.check_dump_traceback_later() | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-22 08:58:55 +02:00
										 |  |  |     def test_dump_traceback_later_repeat(self): | 
					
						
							|  |  |  |         self.check_dump_traceback_later(repeat=True) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-22 08:58:55 +02:00
										 |  |  |     def test_dump_traceback_later_cancel(self): | 
					
						
							|  |  |  |         self.check_dump_traceback_later(cancel=True) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-22 08:58:55 +02:00
										 |  |  |     def test_dump_traceback_later_file(self): | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |         with temporary_filename() as filename: | 
					
						
							|  |  |  |             self.check_dump_traceback_later(filename=filename) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-13 11:01:30 +01:00
										 |  |  |     @unittest.skipIf(sys.platform == "win32", | 
					
						
							|  |  |  |                      "subprocess doesn't support pass_fds on Windows") | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |     def test_dump_traceback_later_fd(self): | 
					
						
							|  |  |  |         with tempfile.TemporaryFile('wb+') as fp: | 
					
						
							|  |  |  |             self.check_dump_traceback_later(fd=fp.fileno()) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-22 08:58:55 +02:00
										 |  |  |     def test_dump_traceback_later_twice(self): | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |         self.check_dump_traceback_later(loops=2) | 
					
						
							| 
									
										
										
										
											2011-04-08 12:57:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     @unittest.skipIf(not hasattr(faulthandler, "register"), | 
					
						
							|  |  |  |                      "need faulthandler.register") | 
					
						
							| 
									
										
										
										
											2011-04-01 12:56:17 +02:00
										 |  |  |     def check_register(self, filename=False, all_threads=False, | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |                        unregister=False, chain=False, fd=None): | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Register a handler displaying the traceback on a user signal. Raise the | 
					
						
							|  |  |  |         signal and check the written traceback. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-13 23:39:53 +02:00
										 |  |  |         If chain is True, check that the previous signal handler is called. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         Raise an error if the output doesn't match the expected format. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2011-04-01 12:56:17 +02:00
										 |  |  |         signum = signal.SIGUSR1 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         code = """
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             import faulthandler | 
					
						
							|  |  |  |             import os | 
					
						
							|  |  |  |             import signal | 
					
						
							|  |  |  |             import sys | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             all_threads = {all_threads} | 
					
						
							| 
									
										
										
										
											2021-05-03 18:44:30 -07:00
										 |  |  |             signum = {signum:d} | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             unregister = {unregister} | 
					
						
							|  |  |  |             chain = {chain} | 
					
						
							|  |  |  |             filename = {filename!r} | 
					
						
							|  |  |  |             fd = {fd} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             def func(signum): | 
					
						
							|  |  |  |                 os.kill(os.getpid(), signum) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             def handler(signum, frame): | 
					
						
							|  |  |  |                 handler.called = True | 
					
						
							|  |  |  |             handler.called = False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             if filename: | 
					
						
							|  |  |  |                 file = open(filename, "wb") | 
					
						
							|  |  |  |             elif fd is not None: | 
					
						
							|  |  |  |                 file = sys.stderr.fileno() | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 file = None | 
					
						
							|  |  |  |             if chain: | 
					
						
							|  |  |  |                 signal.signal(signum, handler) | 
					
						
							|  |  |  |             faulthandler.register(signum, file=file, | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |                                   all_threads=all_threads, chain={chain}) | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |             if unregister: | 
					
						
							|  |  |  |                 faulthandler.unregister(signum) | 
					
						
							|  |  |  |             func(signum) | 
					
						
							|  |  |  |             if chain and not handler.called: | 
					
						
							|  |  |  |                 if file is not None: | 
					
						
							|  |  |  |                     output = file | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     output = sys.stderr | 
					
						
							|  |  |  |                 print("Error: signal handler not called!", file=output) | 
					
						
							|  |  |  |                 exitcode = 1 | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 exitcode = 0 | 
					
						
							|  |  |  |             if filename: | 
					
						
							| 
									
										
										
										
											2014-08-10 19:50:08 +02:00
										 |  |  |                 file.close() | 
					
						
							|  |  |  |             sys.exit(exitcode) | 
					
						
							|  |  |  |             """
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         code = code.format( | 
					
						
							|  |  |  |             all_threads=all_threads, | 
					
						
							| 
									
										
										
										
											2011-04-01 12:56:17 +02:00
										 |  |  |             signum=signum, | 
					
						
							|  |  |  |             unregister=unregister, | 
					
						
							| 
									
										
										
										
											2011-07-13 23:39:53 +02:00
										 |  |  |             chain=chain, | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             filename=filename, | 
					
						
							|  |  |  |             fd=fd, | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2011-03-31 13:29:56 +02:00
										 |  |  |         trace, exitcode = self.get_output(code, filename) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         trace = '\n'.join(trace) | 
					
						
							| 
									
										
										
										
											2011-04-01 12:56:17 +02:00
										 |  |  |         if not unregister: | 
					
						
							|  |  |  |             if all_threads: | 
					
						
							| 
									
										
										
										
											2016-09-08 13:59:53 -04:00
										 |  |  |                 regex = r'Current thread 0x[0-9a-f]+ \(most recent call first\):\n' | 
					
						
							| 
									
										
										
										
											2011-04-01 12:56:17 +02:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2016-09-08 13:59:53 -04:00
										 |  |  |                 regex = r'Stack \(most recent call first\):\n' | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |             regex = expected_traceback(14, 32, regex) | 
					
						
							| 
									
										
										
										
											2011-04-01 12:56:17 +02:00
										 |  |  |             self.assertRegex(trace, regex) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2011-04-01 12:56:17 +02:00
										 |  |  |             self.assertEqual(trace, '') | 
					
						
							|  |  |  |         if unregister: | 
					
						
							|  |  |  |             self.assertNotEqual(exitcode, 0) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.assertEqual(exitcode, 0) | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_register(self): | 
					
						
							|  |  |  |         self.check_register() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-04-01 12:56:17 +02:00
										 |  |  |     def test_unregister(self): | 
					
						
							|  |  |  |         self.check_register(unregister=True) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     def test_register_file(self): | 
					
						
							|  |  |  |         with temporary_filename() as filename: | 
					
						
							|  |  |  |             self.check_register(filename=filename) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-13 11:01:30 +01:00
										 |  |  |     @unittest.skipIf(sys.platform == "win32", | 
					
						
							|  |  |  |                      "subprocess doesn't support pass_fds on Windows") | 
					
						
							| 
									
										
										
										
											2015-03-12 15:32:03 +01:00
										 |  |  |     def test_register_fd(self): | 
					
						
							|  |  |  |         with tempfile.TemporaryFile('wb+') as fp: | 
					
						
							|  |  |  |             self.check_register(fd=fp.fileno()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  |     def test_register_threads(self): | 
					
						
							|  |  |  |         self.check_register(all_threads=True) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-13 23:39:53 +02:00
										 |  |  |     def test_register_chain(self): | 
					
						
							|  |  |  |         self.check_register(chain=True) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-14 17:15:50 +02:00
										 |  |  |     @contextmanager | 
					
						
							|  |  |  |     def check_stderr_none(self): | 
					
						
							|  |  |  |         stderr = sys.stderr | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             sys.stderr = None | 
					
						
							|  |  |  |             with self.assertRaises(RuntimeError) as cm: | 
					
						
							|  |  |  |                 yield | 
					
						
							|  |  |  |             self.assertEqual(str(cm.exception), "sys.stderr is None") | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             sys.stderr = stderr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_stderr_None(self): | 
					
						
							| 
									
										
										
										
											2016-04-17 08:32:47 +03:00
										 |  |  |         # Issue #21497: provide a helpful error if sys.stderr is None, | 
					
						
							| 
									
										
										
										
											2014-05-14 17:15:50 +02:00
										 |  |  |         # instead of just an attribute error: "None has no attribute fileno". | 
					
						
							|  |  |  |         with self.check_stderr_none(): | 
					
						
							|  |  |  |             faulthandler.enable() | 
					
						
							|  |  |  |         with self.check_stderr_none(): | 
					
						
							|  |  |  |             faulthandler.dump_traceback() | 
					
						
							| 
									
										
										
										
											2019-09-18 14:15:10 +02:00
										 |  |  |         with self.check_stderr_none(): | 
					
						
							|  |  |  |             faulthandler.dump_traceback_later(1e-3) | 
					
						
							| 
									
										
										
										
											2014-05-14 17:15:50 +02:00
										 |  |  |         if hasattr(faulthandler, "register"): | 
					
						
							|  |  |  |             with self.check_stderr_none(): | 
					
						
							|  |  |  |                 faulthandler.register(signal.SIGUSR1) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-26 02:11:06 +02:00
										 |  |  |     @unittest.skipUnless(MS_WINDOWS, 'specific to Windows') | 
					
						
							| 
									
										
										
										
											2016-03-23 10:39:17 +01:00
										 |  |  |     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'), | 
					
						
							|  |  |  |         ): | 
					
						
							| 
									
										
										
										
											2016-09-09 21:56:20 -04:00
										 |  |  |             self.check_windows_exception(f"""
 | 
					
						
							| 
									
										
										
										
											2016-03-23 10:39:17 +01:00
										 |  |  |                 import faulthandler | 
					
						
							|  |  |  |                 faulthandler.enable() | 
					
						
							|  |  |  |                 faulthandler._raise_exception(faulthandler._{exc}) | 
					
						
							| 
									
										
										
										
											2016-09-09 21:56:20 -04:00
										 |  |  |                 """,
 | 
					
						
							| 
									
										
										
										
											2016-03-23 10:39:17 +01:00
										 |  |  |                 3, | 
					
						
							|  |  |  |                 name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-26 02:11:06 +02:00
										 |  |  |     @unittest.skipUnless(MS_WINDOWS, 'specific to Windows') | 
					
						
							| 
									
										
										
										
											2017-10-09 09:52:32 -07:00
										 |  |  |     def test_ignore_exception(self): | 
					
						
							|  |  |  |         for exc_code in ( | 
					
						
							|  |  |  |             0xE06D7363,   # MSC exception ("Emsc") | 
					
						
							|  |  |  |             0xE0434352,   # COM Callable Runtime exception ("ECCR") | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             code = f"""
 | 
					
						
							|  |  |  |                     import faulthandler | 
					
						
							|  |  |  |                     faulthandler.enable() | 
					
						
							|  |  |  |                     faulthandler._raise_exception({exc_code}) | 
					
						
							|  |  |  |                     """
 | 
					
						
							|  |  |  |             code = dedent(code) | 
					
						
							|  |  |  |             output, exitcode = self.get_output(code) | 
					
						
							|  |  |  |             self.assertEqual(output, []) | 
					
						
							|  |  |  |             self.assertEqual(exitcode, exc_code) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-26 02:11:06 +02:00
										 |  |  |     @unittest.skipUnless(MS_WINDOWS, 'specific to Windows') | 
					
						
							| 
									
										
										
										
											2017-06-05 15:54:15 -07:00
										 |  |  |     def test_raise_nonfatal_exception(self): | 
					
						
							|  |  |  |         # These exceptions are not strictly errors. Letting | 
					
						
							|  |  |  |         # faulthandler display the traceback when they are | 
					
						
							|  |  |  |         # raised is likely to result in noise. However, they | 
					
						
							|  |  |  |         # may still terminate the process if there is no | 
					
						
							|  |  |  |         # handler installed for them (which there typically | 
					
						
							|  |  |  |         # is, e.g. for debug messages). | 
					
						
							|  |  |  |         for exc in ( | 
					
						
							|  |  |  |             0x00000000, | 
					
						
							|  |  |  |             0x34567890, | 
					
						
							|  |  |  |             0x40000000, | 
					
						
							|  |  |  |             0x40001000, | 
					
						
							|  |  |  |             0x70000000, | 
					
						
							|  |  |  |             0x7FFFFFFF, | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             output, exitcode = self.get_output(f"""
 | 
					
						
							|  |  |  |                 import faulthandler | 
					
						
							|  |  |  |                 faulthandler.enable() | 
					
						
							|  |  |  |                 faulthandler._raise_exception(0x{exc:x}) | 
					
						
							|  |  |  |                 """
 | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |             self.assertEqual(output, []) | 
					
						
							| 
									
										
										
										
											2017-06-06 19:40:41 +02:00
										 |  |  |             # On Windows older than 7 SP1, the actual exception code has | 
					
						
							|  |  |  |             # bit 29 cleared. | 
					
						
							|  |  |  |             self.assertIn(exitcode, | 
					
						
							|  |  |  |                           (exc, exc & ~0x10000000)) | 
					
						
							| 
									
										
										
										
											2017-06-05 15:54:15 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-26 02:11:06 +02:00
										 |  |  |     @unittest.skipUnless(MS_WINDOWS, 'specific to Windows') | 
					
						
							| 
									
										
										
										
											2017-04-21 18:06:13 +02:00
										 |  |  |     def test_disable_windows_exc_handler(self): | 
					
						
							|  |  |  |         code = dedent("""
 | 
					
						
							|  |  |  |             import faulthandler | 
					
						
							|  |  |  |             faulthandler.enable() | 
					
						
							|  |  |  |             faulthandler.disable() | 
					
						
							|  |  |  |             code = faulthandler._EXCEPTION_ACCESS_VIOLATION | 
					
						
							|  |  |  |             faulthandler._raise_exception(code) | 
					
						
							|  |  |  |         """)
 | 
					
						
							|  |  |  |         output, exitcode = self.get_output(code) | 
					
						
							|  |  |  |         self.assertEqual(output, []) | 
					
						
							|  |  |  |         self.assertEqual(exitcode, 0xC0000005) | 
					
						
							| 
									
										
										
										
											2016-03-23 10:39:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-29 12:30:04 -04:00
										 |  |  |     def test_cancel_later_without_dump_traceback_later(self): | 
					
						
							|  |  |  |         # bpo-37933: Calling cancel_dump_traceback_later() | 
					
						
							|  |  |  |         # without dump_traceback_later() must not segfault. | 
					
						
							|  |  |  |         code = dedent("""
 | 
					
						
							|  |  |  |             import faulthandler | 
					
						
							|  |  |  |             faulthandler.cancel_dump_traceback_later() | 
					
						
							|  |  |  |         """)
 | 
					
						
							|  |  |  |         output, exitcode = self.get_output(code) | 
					
						
							|  |  |  |         self.assertEqual(output, []) | 
					
						
							|  |  |  |         self.assertEqual(exitcode, 0) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:31:06 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							| 
									
										
										
										
											2013-06-12 21:25:59 -04:00
										 |  |  |     unittest.main() |