blob: 984690f248baaa8417ec70f5316710d229e4ac9c [file] [log] [blame]
Victor Stinner024e37a2011-03-31 01:31:06 +02001from contextlib import contextmanager
Victor Stinnerc790a532011-04-08 13:39:59 +02002import datetime
Victor Stinner024e37a2011-03-31 01:31:06 +02003import faulthandler
Victor Stinner7e32f3a2011-06-29 13:44:05 +02004import os
Victor Stinner024e37a2011-03-31 01:31:06 +02005import signal
6import subprocess
7import sys
Miss Islington (bot)01b96642018-12-30 17:59:19 -08008import sysconfig
Berker Peksagce643912015-05-06 06:33:17 +03009from test import support
xdegayeef838062017-11-29 11:43:23 +010010from test.support import script_helper, is_android
Victor Stinner024e37a2011-03-31 01:31:06 +020011import tempfile
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020012import threading
Victor Stinner024e37a2011-03-31 01:31:06 +020013import unittest
Victor Stinner6d201682014-08-10 19:50:08 +020014from textwrap import dedent
Victor Stinner024e37a2011-03-31 01:31:06 +020015
Victor Stinnerff4cd882011-04-07 11:50:25 +020016try:
Victor Stinner56e8c292014-07-21 12:30:22 +020017 import _testcapi
18except ImportError:
19 _testcapi = None
Victor Stinnerff4cd882011-04-07 11:50:25 +020020
Victor Stinner44378d42011-04-01 15:37:12 +020021TIMEOUT = 0.5
Victor Stinner404cdc52016-03-23 10:39:17 +010022MS_WINDOWS = (os.name == 'nt')
Miss Islington (bot)01b96642018-12-30 17:59:19 -080023MEMORY_SANITIZER = (
24 sysconfig.get_config_var("CONFIG_ARGS") and
25 ("--with-memory-sanitizer" in sysconfig.get_config_var("CONFIG_ARGS"))
26)
Victor Stinner44378d42011-04-01 15:37:12 +020027
Victor Stinner301f3f02011-06-01 13:49:12 +020028def expected_traceback(lineno1, lineno2, header, min_count=1):
Victor Stinner024e37a2011-03-31 01:31:06 +020029 regex = header
Victor Stinner7ad24e92011-03-31 22:35:49 +020030 regex += ' File "<string>", line %s in func\n' % lineno1
31 regex += ' File "<string>", line %s in <module>' % lineno2
Victor Stinner301f3f02011-06-01 13:49:12 +020032 if 1 < min_count:
33 return '^' + (regex + '\n') * (min_count - 1) + regex
34 else:
35 return '^' + regex + '$'
Victor Stinner024e37a2011-03-31 01:31:06 +020036
xdegayeef838062017-11-29 11:43:23 +010037def skip_segfault_on_android(test):
38 # Issue #32138: Raising SIGSEGV on Android may not cause a crash.
39 return unittest.skipIf(is_android,
40 'raising SIGSEGV on Android is unreliable')(test)
41
Victor Stinner024e37a2011-03-31 01:31:06 +020042@contextmanager
43def temporary_filename():
44 filename = tempfile.mktemp()
45 try:
46 yield filename
47 finally:
48 support.unlink(filename)
49
50class FaultHandlerTests(unittest.TestCase):
Victor Stinner95bb7142015-03-12 15:32:03 +010051 def get_output(self, code, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020052 """
53 Run the specified code in Python (in a new child process) and read the
54 output from the standard error or from a file (if filename is set).
55 Return the output lines as a list.
56
57 Strip the reference count from the standard error for Python debug
58 build, and replace "Current thread 0x00007f8d8fbd9700" by "Current
59 thread XXX".
60 """
Victor Stinner6d201682014-08-10 19:50:08 +020061 code = dedent(code).strip()
Victor Stinner95bb7142015-03-12 15:32:03 +010062 pass_fds = []
63 if fd is not None:
64 pass_fds.append(fd)
Antoine Pitrou77e904e2013-10-08 23:04:32 +020065 with support.SuppressCrashReport():
Victor Stinner95bb7142015-03-12 15:32:03 +010066 process = script_helper.spawn_python('-c', code, pass_fds=pass_fds)
Victor Stinner861d9ab2016-03-16 22:45:24 +010067 with process:
68 stdout, stderr = process.communicate()
69 exitcode = process.wait()
Victor Stinner19402332011-03-31 18:15:52 +020070 output = support.strip_python_stderr(stdout)
71 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020072 if filename:
Victor Stinner19402332011-03-31 18:15:52 +020073 self.assertEqual(output, '')
Victor Stinner024e37a2011-03-31 01:31:06 +020074 with open(filename, "rb") as fp:
75 output = fp.read()
Victor Stinner19402332011-03-31 18:15:52 +020076 output = output.decode('ascii', 'backslashreplace')
Victor Stinner95bb7142015-03-12 15:32:03 +010077 elif fd is not None:
78 self.assertEqual(output, '')
79 os.lseek(fd, os.SEEK_SET, 0)
80 with open(fd, "rb", closefd=False) as fp:
81 output = fp.read()
82 output = output.decode('ascii', 'backslashreplace')
Victor Stinner05585cb2011-03-31 13:29:56 +020083 return output.splitlines(), exitcode
Victor Stinner024e37a2011-03-31 01:31:06 +020084
Victor Stinner404cdc52016-03-23 10:39:17 +010085 def check_error(self, code, line_number, fatal_error, *,
86 filename=None, all_threads=True, other_regex=None,
87 fd=None, know_current_thread=True):
Victor Stinner024e37a2011-03-31 01:31:06 +020088 """
89 Check that the fault handler for fatal errors is enabled and check the
90 traceback from the child process output.
91
92 Raise an error if the output doesn't match the expected format.
93 """
94 if all_threads:
Victor Stinner861d9ab2016-03-16 22:45:24 +010095 if know_current_thread:
96 header = 'Current thread 0x[0-9a-f]+'
97 else:
98 header = 'Thread 0x[0-9a-f]+'
Victor Stinner024e37a2011-03-31 01:31:06 +020099 else:
Victor Stinner861d9ab2016-03-16 22:45:24 +0100100 header = 'Stack'
R David Murray44b548d2016-09-08 13:59:53 -0400101 regex = r"""
Victor Stinner404cdc52016-03-23 10:39:17 +0100102 ^{fatal_error}
Victor Stinner024e37a2011-03-31 01:31:06 +0200103
Victor Stinner861d9ab2016-03-16 22:45:24 +0100104 {header} \(most recent call first\):
Victor Stinner6d201682014-08-10 19:50:08 +0200105 File "<string>", line {lineno} in <module>
106 """
107 regex = dedent(regex.format(
Victor Stinner024e37a2011-03-31 01:31:06 +0200108 lineno=line_number,
Victor Stinner404cdc52016-03-23 10:39:17 +0100109 fatal_error=fatal_error,
Victor Stinner861d9ab2016-03-16 22:45:24 +0100110 header=header)).strip()
Victor Stinnerf0480752011-03-31 11:34:08 +0200111 if other_regex:
112 regex += '|' + other_regex
Victor Stinner95bb7142015-03-12 15:32:03 +0100113 output, exitcode = self.get_output(code, filename=filename, fd=fd)
Victor Stinner024e37a2011-03-31 01:31:06 +0200114 output = '\n'.join(output)
115 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200116 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200117
Victor Stinner404cdc52016-03-23 10:39:17 +0100118 def check_fatal_error(self, code, line_number, name_regex, **kw):
119 fatal_error = 'Fatal Python error: %s' % name_regex
120 self.check_error(code, line_number, fatal_error, **kw)
121
122 def check_windows_exception(self, code, line_number, name_regex, **kw):
Victor Stinner412a5e72016-03-23 14:44:14 +0100123 fatal_error = 'Windows fatal exception: %s' % name_regex
Victor Stinner404cdc52016-03-23 10:39:17 +0100124 self.check_error(code, line_number, fatal_error, **kw)
125
Victor Stinner330426c2013-07-03 22:29:42 +0200126 @unittest.skipIf(sys.platform.startswith('aix'),
127 "the first page of memory is a mapped read-only on AIX")
Victor Stinner024e37a2011-03-31 01:31:06 +0200128 def test_read_null(self):
Victor Stinner404cdc52016-03-23 10:39:17 +0100129 if not MS_WINDOWS:
130 self.check_fatal_error("""
131 import faulthandler
132 faulthandler.enable()
133 faulthandler._read_null()
134 """,
135 3,
136 # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion
137 '(?:Segmentation fault'
138 '|Bus error'
139 '|Illegal instruction)')
140 else:
141 self.check_windows_exception("""
142 import faulthandler
143 faulthandler.enable()
144 faulthandler._read_null()
145 """,
146 3,
147 'access violation')
Victor Stinner024e37a2011-03-31 01:31:06 +0200148
xdegayeef838062017-11-29 11:43:23 +0100149 @skip_segfault_on_android
Victor Stinner024e37a2011-03-31 01:31:06 +0200150 def test_sigsegv(self):
151 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200152 import faulthandler
153 faulthandler.enable()
154 faulthandler._sigsegv()
155 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200156 3,
157 'Segmentation fault')
158
Victor Stinner861d9ab2016-03-16 22:45:24 +0100159 def test_fatal_error_c_thread(self):
160 self.check_fatal_error("""
161 import faulthandler
162 faulthandler.enable()
163 faulthandler._fatal_error_c_thread()
164 """,
165 3,
166 'in new thread',
167 know_current_thread=False)
168
Victor Stinnerd727e232011-04-01 12:13:55 +0200169 def test_sigabrt(self):
170 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200171 import faulthandler
172 faulthandler.enable()
173 faulthandler._sigabrt()
174 """,
Victor Stinnerd727e232011-04-01 12:13:55 +0200175 3,
176 'Aborted')
177
Victor Stinner024e37a2011-03-31 01:31:06 +0200178 @unittest.skipIf(sys.platform == 'win32',
179 "SIGFPE cannot be caught on Windows")
180 def test_sigfpe(self):
181 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200182 import faulthandler
183 faulthandler.enable()
184 faulthandler._sigfpe()
185 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200186 3,
187 'Floating point exception')
188
Victor Stinner56e8c292014-07-21 12:30:22 +0200189 @unittest.skipIf(_testcapi is None, 'need _testcapi')
190 @unittest.skipUnless(hasattr(signal, 'SIGBUS'), 'need signal.SIGBUS')
xdegayeef838062017-11-29 11:43:23 +0100191 @skip_segfault_on_android
Victor Stinner024e37a2011-03-31 01:31:06 +0200192 def test_sigbus(self):
193 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200194 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200195 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200196 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200197
Victor Stinner6d201682014-08-10 19:50:08 +0200198 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200199 _testcapi.raise_signal(signal.SIGBUS)
Victor Stinner6d201682014-08-10 19:50:08 +0200200 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200201 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200202 'Bus error')
203
Victor Stinner56e8c292014-07-21 12:30:22 +0200204 @unittest.skipIf(_testcapi is None, 'need _testcapi')
205 @unittest.skipUnless(hasattr(signal, 'SIGILL'), 'need signal.SIGILL')
xdegayeef838062017-11-29 11:43:23 +0100206 @skip_segfault_on_android
Victor Stinner024e37a2011-03-31 01:31:06 +0200207 def test_sigill(self):
208 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200209 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200210 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200211 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200212
Victor Stinner6d201682014-08-10 19:50:08 +0200213 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200214 _testcapi.raise_signal(signal.SIGILL)
Victor Stinner6d201682014-08-10 19:50:08 +0200215 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200216 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200217 'Illegal instruction')
218
219 def test_fatal_error(self):
220 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200221 import faulthandler
222 faulthandler._fatal_error(b'xyz')
223 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200224 2,
225 'xyz')
226
Victor Stinner57003f82016-03-15 17:23:35 +0100227 def test_fatal_error_without_gil(self):
228 self.check_fatal_error("""
229 import faulthandler
230 faulthandler._fatal_error(b'xyz', True)
231 """,
232 2,
233 'xyz')
234
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200235 @unittest.skipIf(sys.platform.startswith('openbsd'),
Charles-François Natali3391e642011-09-01 23:08:21 +0200236 "Issue #12868: sigaltstack() doesn't work on "
237 "OpenBSD if Python is compiled with pthread")
Victor Stinner024e37a2011-03-31 01:31:06 +0200238 @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'),
239 'need faulthandler._stack_overflow()')
240 def test_stack_overflow(self):
241 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200242 import faulthandler
243 faulthandler.enable()
244 faulthandler._stack_overflow()
245 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200246 3,
Victor Stinnerf0480752011-03-31 11:34:08 +0200247 '(?:Segmentation fault|Bus error)',
248 other_regex='unable to raise a stack overflow')
Victor Stinner024e37a2011-03-31 01:31:06 +0200249
xdegayeef838062017-11-29 11:43:23 +0100250 @skip_segfault_on_android
Victor Stinner024e37a2011-03-31 01:31:06 +0200251 def test_gil_released(self):
252 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200253 import faulthandler
254 faulthandler.enable()
Victor Stinner50838282014-09-30 13:54:14 +0200255 faulthandler._sigsegv(True)
Victor Stinner6d201682014-08-10 19:50:08 +0200256 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200257 3,
Victor Stinner50838282014-09-30 13:54:14 +0200258 'Segmentation fault')
Victor Stinner024e37a2011-03-31 01:31:06 +0200259
Miss Islington (bot)01b96642018-12-30 17:59:19 -0800260 @unittest.skipIf(MEMORY_SANITIZER,
261 "memory-sanizer builds change crashing process output.")
xdegayeef838062017-11-29 11:43:23 +0100262 @skip_segfault_on_android
Victor Stinner024e37a2011-03-31 01:31:06 +0200263 def test_enable_file(self):
264 with temporary_filename() as filename:
265 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200266 import faulthandler
267 output = open({filename}, 'wb')
268 faulthandler.enable(output)
269 faulthandler._sigsegv()
270 """.format(filename=repr(filename)),
Victor Stinner024e37a2011-03-31 01:31:06 +0200271 4,
Victor Stinner56785392013-06-17 23:37:59 +0200272 'Segmentation fault',
Victor Stinner024e37a2011-03-31 01:31:06 +0200273 filename=filename)
274
Victor Stinnerff2a6612015-03-13 11:01:30 +0100275 @unittest.skipIf(sys.platform == "win32",
276 "subprocess doesn't support pass_fds on Windows")
Miss Islington (bot)01b96642018-12-30 17:59:19 -0800277 @unittest.skipIf(MEMORY_SANITIZER,
278 "memory-sanizer builds change crashing process output.")
xdegayeef838062017-11-29 11:43:23 +0100279 @skip_segfault_on_android
Victor Stinner95bb7142015-03-12 15:32:03 +0100280 def test_enable_fd(self):
281 with tempfile.TemporaryFile('wb+') as fp:
282 fd = fp.fileno()
283 self.check_fatal_error("""
284 import faulthandler
285 import sys
286 faulthandler.enable(%s)
287 faulthandler._sigsegv()
288 """ % fd,
289 4,
290 'Segmentation fault',
291 fd=fd)
292
xdegayeef838062017-11-29 11:43:23 +0100293 @skip_segfault_on_android
Victor Stinner7bba62f2011-05-07 12:43:00 +0200294 def test_enable_single_thread(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200295 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200296 import faulthandler
297 faulthandler.enable(all_threads=False)
298 faulthandler._sigsegv()
299 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200300 3,
Victor Stinner56785392013-06-17 23:37:59 +0200301 'Segmentation fault',
Victor Stinner7bba62f2011-05-07 12:43:00 +0200302 all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200303
xdegayeef838062017-11-29 11:43:23 +0100304 @skip_segfault_on_android
Victor Stinner024e37a2011-03-31 01:31:06 +0200305 def test_disable(self):
306 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200307 import faulthandler
308 faulthandler.enable()
309 faulthandler.disable()
310 faulthandler._sigsegv()
311 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200312 not_expected = 'Fatal Python error'
Antoine Pitrou77e904e2013-10-08 23:04:32 +0200313 stderr, exitcode = self.get_output(code)
Victor Stinner29001c82014-09-25 00:38:48 +0200314 stderr = '\n'.join(stderr)
Victor Stinner024e37a2011-03-31 01:31:06 +0200315 self.assertTrue(not_expected not in stderr,
316 "%r is present in %r" % (not_expected, stderr))
Victor Stinner05585cb2011-03-31 13:29:56 +0200317 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200318
319 def test_is_enabled(self):
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200320 orig_stderr = sys.stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200321 try:
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200322 # regrtest may replace sys.stderr by io.StringIO object, but
323 # faulthandler.enable() requires that sys.stderr has a fileno()
324 # method
Victor Stinner72488502011-06-29 23:24:31 +0200325 sys.stderr = sys.__stderr__
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200326
327 was_enabled = faulthandler.is_enabled()
328 try:
Victor Stinner024e37a2011-03-31 01:31:06 +0200329 faulthandler.enable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200330 self.assertTrue(faulthandler.is_enabled())
Victor Stinner024e37a2011-03-31 01:31:06 +0200331 faulthandler.disable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200332 self.assertFalse(faulthandler.is_enabled())
333 finally:
334 if was_enabled:
335 faulthandler.enable()
336 else:
337 faulthandler.disable()
338 finally:
339 sys.stderr = orig_stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200340
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200341 def test_disabled_by_default(self):
342 # By default, the module should be disabled
343 code = "import faulthandler; print(faulthandler.is_enabled())"
Victor Stinner721e25c2017-12-12 23:15:00 +0100344 args = (sys.executable, "-E", "-c", code)
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800345 # don't use assert_python_ok() because it always enables faulthandler
Victor Stinner721e25c2017-12-12 23:15:00 +0100346 output = subprocess.check_output(args)
Victor Stinner88983502013-09-08 11:36:23 +0200347 self.assertEqual(output.rstrip(), b"False")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200348
349 def test_sys_xoptions(self):
350 # Test python -X faulthandler
351 code = "import faulthandler; print(faulthandler.is_enabled())"
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800352 args = filter(None, (sys.executable,
353 "-E" if sys.flags.ignore_environment else "",
354 "-X", "faulthandler", "-c", code))
355 env = os.environ.copy()
356 env.pop("PYTHONFAULTHANDLER", None)
357 # don't use assert_python_ok() because it always enables faulthandler
358 output = subprocess.check_output(args, env=env)
Victor Stinner88983502013-09-08 11:36:23 +0200359 self.assertEqual(output.rstrip(), b"True")
360
361 def test_env_var(self):
362 # empty env var
363 code = "import faulthandler; print(faulthandler.is_enabled())"
364 args = (sys.executable, "-c", code)
Victor Stinner721e25c2017-12-12 23:15:00 +0100365 env = dict(os.environ)
Victor Stinner88983502013-09-08 11:36:23 +0200366 env['PYTHONFAULTHANDLER'] = ''
Victor Stinner721e25c2017-12-12 23:15:00 +0100367 env['PYTHONDEVMODE'] = ''
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800368 # don't use assert_python_ok() because it always enables faulthandler
Victor Stinner88983502013-09-08 11:36:23 +0200369 output = subprocess.check_output(args, env=env)
370 self.assertEqual(output.rstrip(), b"False")
371
372 # non-empty env var
Victor Stinner721e25c2017-12-12 23:15:00 +0100373 env = dict(os.environ)
Victor Stinner88983502013-09-08 11:36:23 +0200374 env['PYTHONFAULTHANDLER'] = '1'
Victor Stinner721e25c2017-12-12 23:15:00 +0100375 env['PYTHONDEVMODE'] = ''
Victor Stinner88983502013-09-08 11:36:23 +0200376 output = subprocess.check_output(args, env=env)
377 self.assertEqual(output.rstrip(), b"True")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200378
Victor Stinner95bb7142015-03-12 15:32:03 +0100379 def check_dump_traceback(self, *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200380 """
381 Explicitly call dump_traceback() function and check its output.
382 Raise an error if the output doesn't match the expected format.
383 """
384 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200385 import faulthandler
Victor Stinner024e37a2011-03-31 01:31:06 +0200386
Victor Stinner95bb7142015-03-12 15:32:03 +0100387 filename = {filename!r}
388 fd = {fd}
389
Victor Stinner6d201682014-08-10 19:50:08 +0200390 def funcB():
Victor Stinner95bb7142015-03-12 15:32:03 +0100391 if filename:
392 with open(filename, "wb") as fp:
Victor Stinner6d201682014-08-10 19:50:08 +0200393 faulthandler.dump_traceback(fp, all_threads=False)
Victor Stinner95bb7142015-03-12 15:32:03 +0100394 elif fd is not None:
395 faulthandler.dump_traceback(fd,
396 all_threads=False)
Victor Stinner6d201682014-08-10 19:50:08 +0200397 else:
398 faulthandler.dump_traceback(all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200399
Victor Stinner6d201682014-08-10 19:50:08 +0200400 def funcA():
401 funcB()
Victor Stinner024e37a2011-03-31 01:31:06 +0200402
Victor Stinner6d201682014-08-10 19:50:08 +0200403 funcA()
404 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200405 code = code.format(
Victor Stinner95bb7142015-03-12 15:32:03 +0100406 filename=filename,
407 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200408 )
409 if filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100410 lineno = 9
411 elif fd is not None:
412 lineno = 12
Victor Stinner024e37a2011-03-31 01:31:06 +0200413 else:
Victor Stinner95bb7142015-03-12 15:32:03 +0100414 lineno = 14
Victor Stinner024e37a2011-03-31 01:31:06 +0200415 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700416 'Stack (most recent call first):',
Victor Stinner024e37a2011-03-31 01:31:06 +0200417 ' File "<string>", line %s in funcB' % lineno,
Victor Stinner95bb7142015-03-12 15:32:03 +0100418 ' File "<string>", line 17 in funcA',
419 ' File "<string>", line 19 in <module>'
Victor Stinner024e37a2011-03-31 01:31:06 +0200420 ]
Victor Stinner95bb7142015-03-12 15:32:03 +0100421 trace, exitcode = self.get_output(code, filename, fd)
Victor Stinner024e37a2011-03-31 01:31:06 +0200422 self.assertEqual(trace, expected)
Victor Stinner05585cb2011-03-31 13:29:56 +0200423 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200424
425 def test_dump_traceback(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100426 self.check_dump_traceback()
Victor Stinner19402332011-03-31 18:15:52 +0200427
428 def test_dump_traceback_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200429 with temporary_filename() as filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100430 self.check_dump_traceback(filename=filename)
431
Victor Stinnerff2a6612015-03-13 11:01:30 +0100432 @unittest.skipIf(sys.platform == "win32",
433 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100434 def test_dump_traceback_fd(self):
435 with tempfile.TemporaryFile('wb+') as fp:
436 self.check_dump_traceback(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200437
Victor Stinner53386d82012-08-01 19:45:34 +0200438 def test_truncate(self):
439 maxlen = 500
440 func_name = 'x' * (maxlen + 50)
441 truncated = 'x' * maxlen + '...'
442 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200443 import faulthandler
Victor Stinner53386d82012-08-01 19:45:34 +0200444
Victor Stinner6d201682014-08-10 19:50:08 +0200445 def {func_name}():
446 faulthandler.dump_traceback(all_threads=False)
Victor Stinner53386d82012-08-01 19:45:34 +0200447
Victor Stinner6d201682014-08-10 19:50:08 +0200448 {func_name}()
449 """
Victor Stinner53386d82012-08-01 19:45:34 +0200450 code = code.format(
451 func_name=func_name,
452 )
453 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700454 'Stack (most recent call first):',
Victor Stinner53386d82012-08-01 19:45:34 +0200455 ' File "<string>", line 4 in %s' % truncated,
456 ' File "<string>", line 6 in <module>'
457 ]
458 trace, exitcode = self.get_output(code)
459 self.assertEqual(trace, expected)
460 self.assertEqual(exitcode, 0)
461
Victor Stinner024e37a2011-03-31 01:31:06 +0200462 def check_dump_traceback_threads(self, filename):
463 """
464 Call explicitly dump_traceback(all_threads=True) and check the output.
465 Raise an error if the output doesn't match the expected format.
466 """
467 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200468 import faulthandler
469 from threading import Thread, Event
470 import time
Victor Stinner024e37a2011-03-31 01:31:06 +0200471
Victor Stinner6d201682014-08-10 19:50:08 +0200472 def dump():
473 if {filename}:
474 with open({filename}, "wb") as fp:
475 faulthandler.dump_traceback(fp, all_threads=True)
476 else:
477 faulthandler.dump_traceback(all_threads=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200478
Victor Stinner6d201682014-08-10 19:50:08 +0200479 class Waiter(Thread):
480 # avoid blocking if the main thread raises an exception.
481 daemon = True
Victor Stinner024e37a2011-03-31 01:31:06 +0200482
Victor Stinner6d201682014-08-10 19:50:08 +0200483 def __init__(self):
484 Thread.__init__(self)
485 self.running = Event()
486 self.stop = Event()
Victor Stinner024e37a2011-03-31 01:31:06 +0200487
Victor Stinner6d201682014-08-10 19:50:08 +0200488 def run(self):
489 self.running.set()
490 self.stop.wait()
Victor Stinner024e37a2011-03-31 01:31:06 +0200491
Victor Stinner6d201682014-08-10 19:50:08 +0200492 waiter = Waiter()
493 waiter.start()
494 waiter.running.wait()
495 dump()
496 waiter.stop.set()
497 waiter.join()
498 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200499 code = code.format(filename=repr(filename))
Victor Stinner05585cb2011-03-31 13:29:56 +0200500 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200501 output = '\n'.join(output)
502 if filename:
503 lineno = 8
504 else:
505 lineno = 10
R David Murray44b548d2016-09-08 13:59:53 -0400506 regex = r"""
Victor Stinner6d201682014-08-10 19:50:08 +0200507 ^Thread 0x[0-9a-f]+ \(most recent call first\):
508 (?: File ".*threading.py", line [0-9]+ in [_a-z]+
509 ){{1,3}} File "<string>", line 23 in run
510 File ".*threading.py", line [0-9]+ in _bootstrap_inner
511 File ".*threading.py", line [0-9]+ in _bootstrap
Victor Stinner024e37a2011-03-31 01:31:06 +0200512
Victor Stinner861d9ab2016-03-16 22:45:24 +0100513 Current thread 0x[0-9a-f]+ \(most recent call first\):
Victor Stinner6d201682014-08-10 19:50:08 +0200514 File "<string>", line {lineno} in dump
515 File "<string>", line 28 in <module>$
516 """
517 regex = dedent(regex.format(lineno=lineno)).strip()
Victor Stinner024e37a2011-03-31 01:31:06 +0200518 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200519 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200520
521 def test_dump_traceback_threads(self):
522 self.check_dump_traceback_threads(None)
Victor Stinner19402332011-03-31 18:15:52 +0200523
524 def test_dump_traceback_threads_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200525 with temporary_filename() as filename:
526 self.check_dump_traceback_threads(filename)
527
Victor Stinner95bb7142015-03-12 15:32:03 +0100528 @unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'),
529 'need faulthandler.dump_traceback_later()')
530 def check_dump_traceback_later(self, repeat=False, cancel=False, loops=1,
531 *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200532 """
533 Check how many times the traceback is written in timeout x 2.5 seconds,
534 or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending
535 on repeat and cancel options.
536
537 Raise an error if the output doesn't match the expect format.
538 """
Victor Stinnerc790a532011-04-08 13:39:59 +0200539 timeout_str = str(datetime.timedelta(seconds=TIMEOUT))
Victor Stinner024e37a2011-03-31 01:31:06 +0200540 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200541 import faulthandler
542 import time
Victor Stinner95bb7142015-03-12 15:32:03 +0100543 import sys
544
545 timeout = {timeout}
546 repeat = {repeat}
547 cancel = {cancel}
548 loops = {loops}
549 filename = {filename!r}
550 fd = {fd}
Victor Stinner024e37a2011-03-31 01:31:06 +0200551
Victor Stinner6d201682014-08-10 19:50:08 +0200552 def func(timeout, repeat, cancel, file, loops):
553 for loop in range(loops):
554 faulthandler.dump_traceback_later(timeout, repeat=repeat, file=file)
555 if cancel:
556 faulthandler.cancel_dump_traceback_later()
557 time.sleep(timeout * 5)
558 faulthandler.cancel_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200559
Victor Stinner95bb7142015-03-12 15:32:03 +0100560 if filename:
561 file = open(filename, "wb")
562 elif fd is not None:
563 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200564 else:
565 file = None
566 func(timeout, repeat, cancel, file, loops)
Victor Stinner95bb7142015-03-12 15:32:03 +0100567 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200568 file.close()
569 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200570 code = code.format(
Victor Stinnerde10f402011-04-08 12:57:06 +0200571 timeout=TIMEOUT,
Victor Stinner024e37a2011-03-31 01:31:06 +0200572 repeat=repeat,
573 cancel=cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200574 loops=loops,
Victor Stinner95bb7142015-03-12 15:32:03 +0100575 filename=filename,
576 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200577 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200578 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200579 trace = '\n'.join(trace)
580
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200581 if not cancel:
Victor Stinnerde10f402011-04-08 12:57:06 +0200582 count = loops
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200583 if repeat:
Victor Stinnerde10f402011-04-08 12:57:06 +0200584 count *= 2
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700585 header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+ \(most recent call first\):\n' % timeout_str
Victor Stinner95bb7142015-03-12 15:32:03 +0100586 regex = expected_traceback(17, 26, header, min_count=count)
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200587 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200588 else:
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200589 self.assertEqual(trace, '')
Victor Stinner05585cb2011-03-31 13:29:56 +0200590 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200591
Georg Brandldeb92b52012-09-22 08:58:55 +0200592 def test_dump_traceback_later(self):
593 self.check_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200594
Georg Brandldeb92b52012-09-22 08:58:55 +0200595 def test_dump_traceback_later_repeat(self):
596 self.check_dump_traceback_later(repeat=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200597
Georg Brandldeb92b52012-09-22 08:58:55 +0200598 def test_dump_traceback_later_cancel(self):
599 self.check_dump_traceback_later(cancel=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200600
Georg Brandldeb92b52012-09-22 08:58:55 +0200601 def test_dump_traceback_later_file(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100602 with temporary_filename() as filename:
603 self.check_dump_traceback_later(filename=filename)
604
Victor Stinnerff2a6612015-03-13 11:01:30 +0100605 @unittest.skipIf(sys.platform == "win32",
606 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100607 def test_dump_traceback_later_fd(self):
608 with tempfile.TemporaryFile('wb+') as fp:
609 self.check_dump_traceback_later(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200610
Georg Brandldeb92b52012-09-22 08:58:55 +0200611 def test_dump_traceback_later_twice(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100612 self.check_dump_traceback_later(loops=2)
Victor Stinnerde10f402011-04-08 12:57:06 +0200613
Victor Stinner024e37a2011-03-31 01:31:06 +0200614 @unittest.skipIf(not hasattr(faulthandler, "register"),
615 "need faulthandler.register")
Victor Stinnera01ca122011-04-01 12:56:17 +0200616 def check_register(self, filename=False, all_threads=False,
Victor Stinner95bb7142015-03-12 15:32:03 +0100617 unregister=False, chain=False, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200618 """
619 Register a handler displaying the traceback on a user signal. Raise the
620 signal and check the written traceback.
621
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200622 If chain is True, check that the previous signal handler is called.
623
Victor Stinner024e37a2011-03-31 01:31:06 +0200624 Raise an error if the output doesn't match the expected format.
625 """
Victor Stinnera01ca122011-04-01 12:56:17 +0200626 signum = signal.SIGUSR1
Victor Stinner024e37a2011-03-31 01:31:06 +0200627 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200628 import faulthandler
629 import os
630 import signal
631 import sys
Victor Stinner024e37a2011-03-31 01:31:06 +0200632
Victor Stinner95bb7142015-03-12 15:32:03 +0100633 all_threads = {all_threads}
634 signum = {signum}
635 unregister = {unregister}
636 chain = {chain}
637 filename = {filename!r}
638 fd = {fd}
639
Victor Stinner6d201682014-08-10 19:50:08 +0200640 def func(signum):
641 os.kill(os.getpid(), signum)
Victor Stinner024e37a2011-03-31 01:31:06 +0200642
Victor Stinner6d201682014-08-10 19:50:08 +0200643 def handler(signum, frame):
644 handler.called = True
645 handler.called = False
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200646
Victor Stinner95bb7142015-03-12 15:32:03 +0100647 if filename:
648 file = open(filename, "wb")
649 elif fd is not None:
650 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200651 else:
652 file = None
653 if chain:
654 signal.signal(signum, handler)
655 faulthandler.register(signum, file=file,
Victor Stinner95bb7142015-03-12 15:32:03 +0100656 all_threads=all_threads, chain={chain})
Victor Stinner6d201682014-08-10 19:50:08 +0200657 if unregister:
658 faulthandler.unregister(signum)
659 func(signum)
660 if chain and not handler.called:
661 if file is not None:
662 output = file
663 else:
664 output = sys.stderr
665 print("Error: signal handler not called!", file=output)
666 exitcode = 1
Victor Stinner95bb7142015-03-12 15:32:03 +0100667 else:
668 exitcode = 0
669 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200670 file.close()
671 sys.exit(exitcode)
672 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200673 code = code.format(
Victor Stinner024e37a2011-03-31 01:31:06 +0200674 all_threads=all_threads,
Victor Stinnera01ca122011-04-01 12:56:17 +0200675 signum=signum,
676 unregister=unregister,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200677 chain=chain,
Victor Stinner95bb7142015-03-12 15:32:03 +0100678 filename=filename,
679 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200680 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200681 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200682 trace = '\n'.join(trace)
Victor Stinnera01ca122011-04-01 12:56:17 +0200683 if not unregister:
684 if all_threads:
R David Murray44b548d2016-09-08 13:59:53 -0400685 regex = r'Current thread 0x[0-9a-f]+ \(most recent call first\):\n'
Victor Stinnera01ca122011-04-01 12:56:17 +0200686 else:
R David Murray44b548d2016-09-08 13:59:53 -0400687 regex = r'Stack \(most recent call first\):\n'
Victor Stinner95bb7142015-03-12 15:32:03 +0100688 regex = expected_traceback(14, 32, regex)
Victor Stinnera01ca122011-04-01 12:56:17 +0200689 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200690 else:
Victor Stinnera01ca122011-04-01 12:56:17 +0200691 self.assertEqual(trace, '')
692 if unregister:
693 self.assertNotEqual(exitcode, 0)
694 else:
695 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200696
697 def test_register(self):
698 self.check_register()
699
Victor Stinnera01ca122011-04-01 12:56:17 +0200700 def test_unregister(self):
701 self.check_register(unregister=True)
702
Victor Stinner024e37a2011-03-31 01:31:06 +0200703 def test_register_file(self):
704 with temporary_filename() as filename:
705 self.check_register(filename=filename)
706
Victor Stinnerff2a6612015-03-13 11:01:30 +0100707 @unittest.skipIf(sys.platform == "win32",
708 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100709 def test_register_fd(self):
710 with tempfile.TemporaryFile('wb+') as fp:
711 self.check_register(fd=fp.fileno())
712
Victor Stinner024e37a2011-03-31 01:31:06 +0200713 def test_register_threads(self):
714 self.check_register(all_threads=True)
715
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200716 def test_register_chain(self):
717 self.check_register(chain=True)
718
Victor Stinnere2d66902014-05-14 17:15:50 +0200719 @contextmanager
720 def check_stderr_none(self):
721 stderr = sys.stderr
722 try:
723 sys.stderr = None
724 with self.assertRaises(RuntimeError) as cm:
725 yield
726 self.assertEqual(str(cm.exception), "sys.stderr is None")
727 finally:
728 sys.stderr = stderr
729
730 def test_stderr_None(self):
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +0300731 # Issue #21497: provide a helpful error if sys.stderr is None,
Victor Stinnere2d66902014-05-14 17:15:50 +0200732 # instead of just an attribute error: "None has no attribute fileno".
733 with self.check_stderr_none():
734 faulthandler.enable()
735 with self.check_stderr_none():
736 faulthandler.dump_traceback()
737 if hasattr(faulthandler, 'dump_traceback_later'):
738 with self.check_stderr_none():
739 faulthandler.dump_traceback_later(1e-3)
740 if hasattr(faulthandler, "register"):
741 with self.check_stderr_none():
742 faulthandler.register(signal.SIGUSR1)
743
Victor Stinner404cdc52016-03-23 10:39:17 +0100744 @unittest.skipUnless(MS_WINDOWS, 'specific to Windows')
745 def test_raise_exception(self):
746 for exc, name in (
747 ('EXCEPTION_ACCESS_VIOLATION', 'access violation'),
748 ('EXCEPTION_INT_DIVIDE_BY_ZERO', 'int divide by zero'),
749 ('EXCEPTION_STACK_OVERFLOW', 'stack overflow'),
750 ):
Eric V. Smith451d0e32016-09-09 21:56:20 -0400751 self.check_windows_exception(f"""
Victor Stinner404cdc52016-03-23 10:39:17 +0100752 import faulthandler
753 faulthandler.enable()
754 faulthandler._raise_exception(faulthandler._{exc})
Eric V. Smith451d0e32016-09-09 21:56:20 -0400755 """,
Victor Stinner404cdc52016-03-23 10:39:17 +0100756 3,
757 name)
758
Victor Stinner46c2b812017-04-21 18:06:13 +0200759 @unittest.skipUnless(MS_WINDOWS, 'specific to Windows')
Victor Stinner6e3d6b52017-10-09 09:52:32 -0700760 def test_ignore_exception(self):
761 for exc_code in (
762 0xE06D7363, # MSC exception ("Emsc")
763 0xE0434352, # COM Callable Runtime exception ("ECCR")
764 ):
765 code = f"""
766 import faulthandler
767 faulthandler.enable()
768 faulthandler._raise_exception({exc_code})
769 """
770 code = dedent(code)
771 output, exitcode = self.get_output(code)
772 self.assertEqual(output, [])
773 self.assertEqual(exitcode, exc_code)
774
775 @unittest.skipUnless(MS_WINDOWS, 'specific to Windows')
Steve Dowere6a23c82017-06-05 15:54:15 -0700776 def test_raise_nonfatal_exception(self):
777 # These exceptions are not strictly errors. Letting
778 # faulthandler display the traceback when they are
779 # raised is likely to result in noise. However, they
780 # may still terminate the process if there is no
781 # handler installed for them (which there typically
782 # is, e.g. for debug messages).
783 for exc in (
784 0x00000000,
785 0x34567890,
786 0x40000000,
787 0x40001000,
788 0x70000000,
789 0x7FFFFFFF,
790 ):
791 output, exitcode = self.get_output(f"""
792 import faulthandler
793 faulthandler.enable()
794 faulthandler._raise_exception(0x{exc:x})
795 """
796 )
797 self.assertEqual(output, [])
Victor Stinner6a1d84e2017-06-06 19:40:41 +0200798 # On Windows older than 7 SP1, the actual exception code has
799 # bit 29 cleared.
800 self.assertIn(exitcode,
801 (exc, exc & ~0x10000000))
Steve Dowere6a23c82017-06-05 15:54:15 -0700802
803 @unittest.skipUnless(MS_WINDOWS, 'specific to Windows')
Victor Stinner46c2b812017-04-21 18:06:13 +0200804 def test_disable_windows_exc_handler(self):
805 code = dedent("""
806 import faulthandler
807 faulthandler.enable()
808 faulthandler.disable()
809 code = faulthandler._EXCEPTION_ACCESS_VIOLATION
810 faulthandler._raise_exception(code)
811 """)
812 output, exitcode = self.get_output(code)
813 self.assertEqual(output, [])
814 self.assertEqual(exitcode, 0xC0000005)
Victor Stinner404cdc52016-03-23 10:39:17 +0100815
Victor Stinner024e37a2011-03-31 01:31:06 +0200816
Victor Stinner024e37a2011-03-31 01:31:06 +0200817if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400818 unittest.main()