blob: d6dc4ba55d9b3e89a8602ac3d3beb34fccbe41d7 [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
Berker Peksagce643912015-05-06 06:33:17 +03008from test import support
xdegayeef838062017-11-29 11:43:23 +01009from test.support import script_helper, is_android
Victor Stinner024e37a2011-03-31 01:31:06 +020010import tempfile
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020011import threading
Victor Stinner024e37a2011-03-31 01:31:06 +020012import unittest
Victor Stinner6d201682014-08-10 19:50:08 +020013from textwrap import dedent
Victor Stinner024e37a2011-03-31 01:31:06 +020014
Victor Stinnerff4cd882011-04-07 11:50:25 +020015try:
Victor Stinner56e8c292014-07-21 12:30:22 +020016 import _testcapi
17except ImportError:
18 _testcapi = None
Victor Stinnerff4cd882011-04-07 11:50:25 +020019
Victor Stinner44378d42011-04-01 15:37:12 +020020TIMEOUT = 0.5
Victor Stinner404cdc52016-03-23 10:39:17 +010021MS_WINDOWS = (os.name == 'nt')
Victor Stinner44378d42011-04-01 15:37:12 +020022
Victor Stinner301f3f02011-06-01 13:49:12 +020023def expected_traceback(lineno1, lineno2, header, min_count=1):
Victor Stinner024e37a2011-03-31 01:31:06 +020024 regex = header
Victor Stinner7ad24e92011-03-31 22:35:49 +020025 regex += ' File "<string>", line %s in func\n' % lineno1
26 regex += ' File "<string>", line %s in <module>' % lineno2
Victor Stinner301f3f02011-06-01 13:49:12 +020027 if 1 < min_count:
28 return '^' + (regex + '\n') * (min_count - 1) + regex
29 else:
30 return '^' + regex + '$'
Victor Stinner024e37a2011-03-31 01:31:06 +020031
xdegayeef838062017-11-29 11:43:23 +010032def skip_segfault_on_android(test):
33 # Issue #32138: Raising SIGSEGV on Android may not cause a crash.
34 return unittest.skipIf(is_android,
35 'raising SIGSEGV on Android is unreliable')(test)
36
Victor Stinner024e37a2011-03-31 01:31:06 +020037@contextmanager
38def temporary_filename():
39 filename = tempfile.mktemp()
40 try:
41 yield filename
42 finally:
43 support.unlink(filename)
44
45class FaultHandlerTests(unittest.TestCase):
Victor Stinner95bb7142015-03-12 15:32:03 +010046 def get_output(self, code, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020047 """
48 Run the specified code in Python (in a new child process) and read the
49 output from the standard error or from a file (if filename is set).
50 Return the output lines as a list.
51
52 Strip the reference count from the standard error for Python debug
53 build, and replace "Current thread 0x00007f8d8fbd9700" by "Current
54 thread XXX".
55 """
Victor Stinner6d201682014-08-10 19:50:08 +020056 code = dedent(code).strip()
Victor Stinner95bb7142015-03-12 15:32:03 +010057 pass_fds = []
58 if fd is not None:
59 pass_fds.append(fd)
Antoine Pitrou77e904e2013-10-08 23:04:32 +020060 with support.SuppressCrashReport():
Victor Stinner95bb7142015-03-12 15:32:03 +010061 process = script_helper.spawn_python('-c', code, pass_fds=pass_fds)
Victor Stinner861d9ab2016-03-16 22:45:24 +010062 with process:
63 stdout, stderr = process.communicate()
64 exitcode = process.wait()
Victor Stinner19402332011-03-31 18:15:52 +020065 output = support.strip_python_stderr(stdout)
66 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020067 if filename:
Victor Stinner19402332011-03-31 18:15:52 +020068 self.assertEqual(output, '')
Victor Stinner024e37a2011-03-31 01:31:06 +020069 with open(filename, "rb") as fp:
70 output = fp.read()
Victor Stinner19402332011-03-31 18:15:52 +020071 output = output.decode('ascii', 'backslashreplace')
Victor Stinner95bb7142015-03-12 15:32:03 +010072 elif fd is not None:
73 self.assertEqual(output, '')
74 os.lseek(fd, os.SEEK_SET, 0)
75 with open(fd, "rb", closefd=False) as fp:
76 output = fp.read()
77 output = output.decode('ascii', 'backslashreplace')
Victor Stinner05585cb2011-03-31 13:29:56 +020078 return output.splitlines(), exitcode
Victor Stinner024e37a2011-03-31 01:31:06 +020079
Victor Stinner404cdc52016-03-23 10:39:17 +010080 def check_error(self, code, line_number, fatal_error, *,
81 filename=None, all_threads=True, other_regex=None,
82 fd=None, know_current_thread=True):
Victor Stinner024e37a2011-03-31 01:31:06 +020083 """
84 Check that the fault handler for fatal errors is enabled and check the
85 traceback from the child process output.
86
87 Raise an error if the output doesn't match the expected format.
88 """
89 if all_threads:
Victor Stinner861d9ab2016-03-16 22:45:24 +010090 if know_current_thread:
91 header = 'Current thread 0x[0-9a-f]+'
92 else:
93 header = 'Thread 0x[0-9a-f]+'
Victor Stinner024e37a2011-03-31 01:31:06 +020094 else:
Victor Stinner861d9ab2016-03-16 22:45:24 +010095 header = 'Stack'
R David Murray44b548d2016-09-08 13:59:53 -040096 regex = r"""
Victor Stinner404cdc52016-03-23 10:39:17 +010097 ^{fatal_error}
Victor Stinner024e37a2011-03-31 01:31:06 +020098
Victor Stinner861d9ab2016-03-16 22:45:24 +010099 {header} \(most recent call first\):
Victor Stinner6d201682014-08-10 19:50:08 +0200100 File "<string>", line {lineno} in <module>
101 """
102 regex = dedent(regex.format(
Victor Stinner024e37a2011-03-31 01:31:06 +0200103 lineno=line_number,
Victor Stinner404cdc52016-03-23 10:39:17 +0100104 fatal_error=fatal_error,
Victor Stinner861d9ab2016-03-16 22:45:24 +0100105 header=header)).strip()
Victor Stinnerf0480752011-03-31 11:34:08 +0200106 if other_regex:
107 regex += '|' + other_regex
Victor Stinner95bb7142015-03-12 15:32:03 +0100108 output, exitcode = self.get_output(code, filename=filename, fd=fd)
Victor Stinner024e37a2011-03-31 01:31:06 +0200109 output = '\n'.join(output)
110 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200111 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200112
Victor Stinner404cdc52016-03-23 10:39:17 +0100113 def check_fatal_error(self, code, line_number, name_regex, **kw):
114 fatal_error = 'Fatal Python error: %s' % name_regex
115 self.check_error(code, line_number, fatal_error, **kw)
116
117 def check_windows_exception(self, code, line_number, name_regex, **kw):
Victor Stinner412a5e72016-03-23 14:44:14 +0100118 fatal_error = 'Windows fatal exception: %s' % name_regex
Victor Stinner404cdc52016-03-23 10:39:17 +0100119 self.check_error(code, line_number, fatal_error, **kw)
120
Victor Stinner330426c2013-07-03 22:29:42 +0200121 @unittest.skipIf(sys.platform.startswith('aix'),
122 "the first page of memory is a mapped read-only on AIX")
Victor Stinner024e37a2011-03-31 01:31:06 +0200123 def test_read_null(self):
Victor Stinner404cdc52016-03-23 10:39:17 +0100124 if not MS_WINDOWS:
125 self.check_fatal_error("""
126 import faulthandler
127 faulthandler.enable()
128 faulthandler._read_null()
129 """,
130 3,
131 # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion
132 '(?:Segmentation fault'
133 '|Bus error'
134 '|Illegal instruction)')
135 else:
136 self.check_windows_exception("""
137 import faulthandler
138 faulthandler.enable()
139 faulthandler._read_null()
140 """,
141 3,
142 'access violation')
Victor Stinner024e37a2011-03-31 01:31:06 +0200143
xdegayeef838062017-11-29 11:43:23 +0100144 @skip_segfault_on_android
Victor Stinner024e37a2011-03-31 01:31:06 +0200145 def test_sigsegv(self):
146 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200147 import faulthandler
148 faulthandler.enable()
149 faulthandler._sigsegv()
150 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200151 3,
152 'Segmentation fault')
153
Victor Stinner861d9ab2016-03-16 22:45:24 +0100154 def test_fatal_error_c_thread(self):
155 self.check_fatal_error("""
156 import faulthandler
157 faulthandler.enable()
158 faulthandler._fatal_error_c_thread()
159 """,
160 3,
161 'in new thread',
162 know_current_thread=False)
163
Victor Stinnerd727e232011-04-01 12:13:55 +0200164 def test_sigabrt(self):
165 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200166 import faulthandler
167 faulthandler.enable()
168 faulthandler._sigabrt()
169 """,
Victor Stinnerd727e232011-04-01 12:13:55 +0200170 3,
171 'Aborted')
172
Victor Stinner024e37a2011-03-31 01:31:06 +0200173 @unittest.skipIf(sys.platform == 'win32',
174 "SIGFPE cannot be caught on Windows")
175 def test_sigfpe(self):
176 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200177 import faulthandler
178 faulthandler.enable()
179 faulthandler._sigfpe()
180 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200181 3,
182 'Floating point exception')
183
Victor Stinner56e8c292014-07-21 12:30:22 +0200184 @unittest.skipIf(_testcapi is None, 'need _testcapi')
185 @unittest.skipUnless(hasattr(signal, 'SIGBUS'), 'need signal.SIGBUS')
xdegayeef838062017-11-29 11:43:23 +0100186 @skip_segfault_on_android
Victor Stinner024e37a2011-03-31 01:31:06 +0200187 def test_sigbus(self):
188 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200189 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200190 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200191 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200192
Victor Stinner6d201682014-08-10 19:50:08 +0200193 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200194 _testcapi.raise_signal(signal.SIGBUS)
Victor Stinner6d201682014-08-10 19:50:08 +0200195 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200196 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200197 'Bus error')
198
Victor Stinner56e8c292014-07-21 12:30:22 +0200199 @unittest.skipIf(_testcapi is None, 'need _testcapi')
200 @unittest.skipUnless(hasattr(signal, 'SIGILL'), 'need signal.SIGILL')
xdegayeef838062017-11-29 11:43:23 +0100201 @skip_segfault_on_android
Victor Stinner024e37a2011-03-31 01:31:06 +0200202 def test_sigill(self):
203 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200204 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200205 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200206 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200207
Victor Stinner6d201682014-08-10 19:50:08 +0200208 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200209 _testcapi.raise_signal(signal.SIGILL)
Victor Stinner6d201682014-08-10 19:50:08 +0200210 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200211 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200212 'Illegal instruction')
213
214 def test_fatal_error(self):
215 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200216 import faulthandler
217 faulthandler._fatal_error(b'xyz')
218 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200219 2,
220 'xyz')
221
Victor Stinner57003f82016-03-15 17:23:35 +0100222 def test_fatal_error_without_gil(self):
223 self.check_fatal_error("""
224 import faulthandler
225 faulthandler._fatal_error(b'xyz', True)
226 """,
227 2,
228 'xyz')
229
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200230 @unittest.skipIf(sys.platform.startswith('openbsd'),
Charles-François Natali3391e642011-09-01 23:08:21 +0200231 "Issue #12868: sigaltstack() doesn't work on "
232 "OpenBSD if Python is compiled with pthread")
Victor Stinner024e37a2011-03-31 01:31:06 +0200233 @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'),
234 'need faulthandler._stack_overflow()')
235 def test_stack_overflow(self):
236 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200237 import faulthandler
238 faulthandler.enable()
239 faulthandler._stack_overflow()
240 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200241 3,
Victor Stinnerf0480752011-03-31 11:34:08 +0200242 '(?:Segmentation fault|Bus error)',
243 other_regex='unable to raise a stack overflow')
Victor Stinner024e37a2011-03-31 01:31:06 +0200244
xdegayeef838062017-11-29 11:43:23 +0100245 @skip_segfault_on_android
Victor Stinner024e37a2011-03-31 01:31:06 +0200246 def test_gil_released(self):
247 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200248 import faulthandler
249 faulthandler.enable()
Victor Stinner50838282014-09-30 13:54:14 +0200250 faulthandler._sigsegv(True)
Victor Stinner6d201682014-08-10 19:50:08 +0200251 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200252 3,
Victor Stinner50838282014-09-30 13:54:14 +0200253 'Segmentation fault')
Victor Stinner024e37a2011-03-31 01:31:06 +0200254
xdegayeef838062017-11-29 11:43:23 +0100255 @skip_segfault_on_android
Victor Stinner024e37a2011-03-31 01:31:06 +0200256 def test_enable_file(self):
257 with temporary_filename() as filename:
258 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200259 import faulthandler
260 output = open({filename}, 'wb')
261 faulthandler.enable(output)
262 faulthandler._sigsegv()
263 """.format(filename=repr(filename)),
Victor Stinner024e37a2011-03-31 01:31:06 +0200264 4,
Victor Stinner56785392013-06-17 23:37:59 +0200265 'Segmentation fault',
Victor Stinner024e37a2011-03-31 01:31:06 +0200266 filename=filename)
267
Victor Stinnerff2a6612015-03-13 11:01:30 +0100268 @unittest.skipIf(sys.platform == "win32",
269 "subprocess doesn't support pass_fds on Windows")
xdegayeef838062017-11-29 11:43:23 +0100270 @skip_segfault_on_android
Victor Stinner95bb7142015-03-12 15:32:03 +0100271 def test_enable_fd(self):
272 with tempfile.TemporaryFile('wb+') as fp:
273 fd = fp.fileno()
274 self.check_fatal_error("""
275 import faulthandler
276 import sys
277 faulthandler.enable(%s)
278 faulthandler._sigsegv()
279 """ % fd,
280 4,
281 'Segmentation fault',
282 fd=fd)
283
xdegayeef838062017-11-29 11:43:23 +0100284 @skip_segfault_on_android
Victor Stinner7bba62f2011-05-07 12:43:00 +0200285 def test_enable_single_thread(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200286 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200287 import faulthandler
288 faulthandler.enable(all_threads=False)
289 faulthandler._sigsegv()
290 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200291 3,
Victor Stinner56785392013-06-17 23:37:59 +0200292 'Segmentation fault',
Victor Stinner7bba62f2011-05-07 12:43:00 +0200293 all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200294
xdegayeef838062017-11-29 11:43:23 +0100295 @skip_segfault_on_android
Victor Stinner024e37a2011-03-31 01:31:06 +0200296 def test_disable(self):
297 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200298 import faulthandler
299 faulthandler.enable()
300 faulthandler.disable()
301 faulthandler._sigsegv()
302 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200303 not_expected = 'Fatal Python error'
Antoine Pitrou77e904e2013-10-08 23:04:32 +0200304 stderr, exitcode = self.get_output(code)
Victor Stinner29001c82014-09-25 00:38:48 +0200305 stderr = '\n'.join(stderr)
Victor Stinner024e37a2011-03-31 01:31:06 +0200306 self.assertTrue(not_expected not in stderr,
307 "%r is present in %r" % (not_expected, stderr))
Victor Stinner05585cb2011-03-31 13:29:56 +0200308 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200309
310 def test_is_enabled(self):
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200311 orig_stderr = sys.stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200312 try:
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200313 # regrtest may replace sys.stderr by io.StringIO object, but
314 # faulthandler.enable() requires that sys.stderr has a fileno()
315 # method
Victor Stinner72488502011-06-29 23:24:31 +0200316 sys.stderr = sys.__stderr__
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200317
318 was_enabled = faulthandler.is_enabled()
319 try:
Victor Stinner024e37a2011-03-31 01:31:06 +0200320 faulthandler.enable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200321 self.assertTrue(faulthandler.is_enabled())
Victor Stinner024e37a2011-03-31 01:31:06 +0200322 faulthandler.disable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200323 self.assertFalse(faulthandler.is_enabled())
324 finally:
325 if was_enabled:
326 faulthandler.enable()
327 else:
328 faulthandler.disable()
329 finally:
330 sys.stderr = orig_stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200331
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200332 def test_disabled_by_default(self):
333 # By default, the module should be disabled
334 code = "import faulthandler; print(faulthandler.is_enabled())"
Victor Stinner721e25c2017-12-12 23:15:00 +0100335 args = (sys.executable, "-E", "-c", code)
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800336 # don't use assert_python_ok() because it always enables faulthandler
Victor Stinner721e25c2017-12-12 23:15:00 +0100337 output = subprocess.check_output(args)
Victor Stinner88983502013-09-08 11:36:23 +0200338 self.assertEqual(output.rstrip(), b"False")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200339
340 def test_sys_xoptions(self):
341 # Test python -X faulthandler
342 code = "import faulthandler; print(faulthandler.is_enabled())"
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800343 args = filter(None, (sys.executable,
344 "-E" if sys.flags.ignore_environment else "",
345 "-X", "faulthandler", "-c", code))
346 env = os.environ.copy()
347 env.pop("PYTHONFAULTHANDLER", None)
348 # don't use assert_python_ok() because it always enables faulthandler
349 output = subprocess.check_output(args, env=env)
Victor Stinner88983502013-09-08 11:36:23 +0200350 self.assertEqual(output.rstrip(), b"True")
351
352 def test_env_var(self):
353 # empty env var
354 code = "import faulthandler; print(faulthandler.is_enabled())"
355 args = (sys.executable, "-c", code)
Victor Stinner721e25c2017-12-12 23:15:00 +0100356 env = dict(os.environ)
Victor Stinner88983502013-09-08 11:36:23 +0200357 env['PYTHONFAULTHANDLER'] = ''
Victor Stinner721e25c2017-12-12 23:15:00 +0100358 env['PYTHONDEVMODE'] = ''
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800359 # don't use assert_python_ok() because it always enables faulthandler
Victor Stinner88983502013-09-08 11:36:23 +0200360 output = subprocess.check_output(args, env=env)
361 self.assertEqual(output.rstrip(), b"False")
362
363 # non-empty env var
Victor Stinner721e25c2017-12-12 23:15:00 +0100364 env = dict(os.environ)
Victor Stinner88983502013-09-08 11:36:23 +0200365 env['PYTHONFAULTHANDLER'] = '1'
Victor Stinner721e25c2017-12-12 23:15:00 +0100366 env['PYTHONDEVMODE'] = ''
Victor Stinner88983502013-09-08 11:36:23 +0200367 output = subprocess.check_output(args, env=env)
368 self.assertEqual(output.rstrip(), b"True")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200369
Victor Stinner95bb7142015-03-12 15:32:03 +0100370 def check_dump_traceback(self, *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200371 """
372 Explicitly call dump_traceback() function and check its output.
373 Raise an error if the output doesn't match the expected format.
374 """
375 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200376 import faulthandler
Victor Stinner024e37a2011-03-31 01:31:06 +0200377
Victor Stinner95bb7142015-03-12 15:32:03 +0100378 filename = {filename!r}
379 fd = {fd}
380
Victor Stinner6d201682014-08-10 19:50:08 +0200381 def funcB():
Victor Stinner95bb7142015-03-12 15:32:03 +0100382 if filename:
383 with open(filename, "wb") as fp:
Victor Stinner6d201682014-08-10 19:50:08 +0200384 faulthandler.dump_traceback(fp, all_threads=False)
Victor Stinner95bb7142015-03-12 15:32:03 +0100385 elif fd is not None:
386 faulthandler.dump_traceback(fd,
387 all_threads=False)
Victor Stinner6d201682014-08-10 19:50:08 +0200388 else:
389 faulthandler.dump_traceback(all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200390
Victor Stinner6d201682014-08-10 19:50:08 +0200391 def funcA():
392 funcB()
Victor Stinner024e37a2011-03-31 01:31:06 +0200393
Victor Stinner6d201682014-08-10 19:50:08 +0200394 funcA()
395 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200396 code = code.format(
Victor Stinner95bb7142015-03-12 15:32:03 +0100397 filename=filename,
398 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200399 )
400 if filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100401 lineno = 9
402 elif fd is not None:
403 lineno = 12
Victor Stinner024e37a2011-03-31 01:31:06 +0200404 else:
Victor Stinner95bb7142015-03-12 15:32:03 +0100405 lineno = 14
Victor Stinner024e37a2011-03-31 01:31:06 +0200406 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700407 'Stack (most recent call first):',
Victor Stinner024e37a2011-03-31 01:31:06 +0200408 ' File "<string>", line %s in funcB' % lineno,
Victor Stinner95bb7142015-03-12 15:32:03 +0100409 ' File "<string>", line 17 in funcA',
410 ' File "<string>", line 19 in <module>'
Victor Stinner024e37a2011-03-31 01:31:06 +0200411 ]
Victor Stinner95bb7142015-03-12 15:32:03 +0100412 trace, exitcode = self.get_output(code, filename, fd)
Victor Stinner024e37a2011-03-31 01:31:06 +0200413 self.assertEqual(trace, expected)
Victor Stinner05585cb2011-03-31 13:29:56 +0200414 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200415
416 def test_dump_traceback(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100417 self.check_dump_traceback()
Victor Stinner19402332011-03-31 18:15:52 +0200418
419 def test_dump_traceback_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200420 with temporary_filename() as filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100421 self.check_dump_traceback(filename=filename)
422
Victor Stinnerff2a6612015-03-13 11:01:30 +0100423 @unittest.skipIf(sys.platform == "win32",
424 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100425 def test_dump_traceback_fd(self):
426 with tempfile.TemporaryFile('wb+') as fp:
427 self.check_dump_traceback(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200428
Victor Stinner53386d82012-08-01 19:45:34 +0200429 def test_truncate(self):
430 maxlen = 500
431 func_name = 'x' * (maxlen + 50)
432 truncated = 'x' * maxlen + '...'
433 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200434 import faulthandler
Victor Stinner53386d82012-08-01 19:45:34 +0200435
Victor Stinner6d201682014-08-10 19:50:08 +0200436 def {func_name}():
437 faulthandler.dump_traceback(all_threads=False)
Victor Stinner53386d82012-08-01 19:45:34 +0200438
Victor Stinner6d201682014-08-10 19:50:08 +0200439 {func_name}()
440 """
Victor Stinner53386d82012-08-01 19:45:34 +0200441 code = code.format(
442 func_name=func_name,
443 )
444 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700445 'Stack (most recent call first):',
Victor Stinner53386d82012-08-01 19:45:34 +0200446 ' File "<string>", line 4 in %s' % truncated,
447 ' File "<string>", line 6 in <module>'
448 ]
449 trace, exitcode = self.get_output(code)
450 self.assertEqual(trace, expected)
451 self.assertEqual(exitcode, 0)
452
Victor Stinner024e37a2011-03-31 01:31:06 +0200453 def check_dump_traceback_threads(self, filename):
454 """
455 Call explicitly dump_traceback(all_threads=True) and check the output.
456 Raise an error if the output doesn't match the expected format.
457 """
458 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200459 import faulthandler
460 from threading import Thread, Event
461 import time
Victor Stinner024e37a2011-03-31 01:31:06 +0200462
Victor Stinner6d201682014-08-10 19:50:08 +0200463 def dump():
464 if {filename}:
465 with open({filename}, "wb") as fp:
466 faulthandler.dump_traceback(fp, all_threads=True)
467 else:
468 faulthandler.dump_traceback(all_threads=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200469
Victor Stinner6d201682014-08-10 19:50:08 +0200470 class Waiter(Thread):
471 # avoid blocking if the main thread raises an exception.
472 daemon = True
Victor Stinner024e37a2011-03-31 01:31:06 +0200473
Victor Stinner6d201682014-08-10 19:50:08 +0200474 def __init__(self):
475 Thread.__init__(self)
476 self.running = Event()
477 self.stop = Event()
Victor Stinner024e37a2011-03-31 01:31:06 +0200478
Victor Stinner6d201682014-08-10 19:50:08 +0200479 def run(self):
480 self.running.set()
481 self.stop.wait()
Victor Stinner024e37a2011-03-31 01:31:06 +0200482
Victor Stinner6d201682014-08-10 19:50:08 +0200483 waiter = Waiter()
484 waiter.start()
485 waiter.running.wait()
486 dump()
487 waiter.stop.set()
488 waiter.join()
489 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200490 code = code.format(filename=repr(filename))
Victor Stinner05585cb2011-03-31 13:29:56 +0200491 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200492 output = '\n'.join(output)
493 if filename:
494 lineno = 8
495 else:
496 lineno = 10
R David Murray44b548d2016-09-08 13:59:53 -0400497 regex = r"""
Victor Stinner6d201682014-08-10 19:50:08 +0200498 ^Thread 0x[0-9a-f]+ \(most recent call first\):
499 (?: File ".*threading.py", line [0-9]+ in [_a-z]+
500 ){{1,3}} File "<string>", line 23 in run
501 File ".*threading.py", line [0-9]+ in _bootstrap_inner
502 File ".*threading.py", line [0-9]+ in _bootstrap
Victor Stinner024e37a2011-03-31 01:31:06 +0200503
Victor Stinner861d9ab2016-03-16 22:45:24 +0100504 Current thread 0x[0-9a-f]+ \(most recent call first\):
Victor Stinner6d201682014-08-10 19:50:08 +0200505 File "<string>", line {lineno} in dump
506 File "<string>", line 28 in <module>$
507 """
508 regex = dedent(regex.format(lineno=lineno)).strip()
Victor Stinner024e37a2011-03-31 01:31:06 +0200509 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200510 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200511
512 def test_dump_traceback_threads(self):
513 self.check_dump_traceback_threads(None)
Victor Stinner19402332011-03-31 18:15:52 +0200514
515 def test_dump_traceback_threads_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200516 with temporary_filename() as filename:
517 self.check_dump_traceback_threads(filename)
518
Victor Stinner95bb7142015-03-12 15:32:03 +0100519 @unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'),
520 'need faulthandler.dump_traceback_later()')
521 def check_dump_traceback_later(self, repeat=False, cancel=False, loops=1,
522 *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200523 """
524 Check how many times the traceback is written in timeout x 2.5 seconds,
525 or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending
526 on repeat and cancel options.
527
528 Raise an error if the output doesn't match the expect format.
529 """
Victor Stinnerc790a532011-04-08 13:39:59 +0200530 timeout_str = str(datetime.timedelta(seconds=TIMEOUT))
Victor Stinner024e37a2011-03-31 01:31:06 +0200531 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200532 import faulthandler
533 import time
Victor Stinner95bb7142015-03-12 15:32:03 +0100534 import sys
535
536 timeout = {timeout}
537 repeat = {repeat}
538 cancel = {cancel}
539 loops = {loops}
540 filename = {filename!r}
541 fd = {fd}
Victor Stinner024e37a2011-03-31 01:31:06 +0200542
Victor Stinner6d201682014-08-10 19:50:08 +0200543 def func(timeout, repeat, cancel, file, loops):
544 for loop in range(loops):
545 faulthandler.dump_traceback_later(timeout, repeat=repeat, file=file)
546 if cancel:
547 faulthandler.cancel_dump_traceback_later()
548 time.sleep(timeout * 5)
549 faulthandler.cancel_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200550
Victor Stinner95bb7142015-03-12 15:32:03 +0100551 if filename:
552 file = open(filename, "wb")
553 elif fd is not None:
554 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200555 else:
556 file = None
557 func(timeout, repeat, cancel, file, loops)
Victor Stinner95bb7142015-03-12 15:32:03 +0100558 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200559 file.close()
560 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200561 code = code.format(
Victor Stinnerde10f402011-04-08 12:57:06 +0200562 timeout=TIMEOUT,
Victor Stinner024e37a2011-03-31 01:31:06 +0200563 repeat=repeat,
564 cancel=cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200565 loops=loops,
Victor Stinner95bb7142015-03-12 15:32:03 +0100566 filename=filename,
567 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200568 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200569 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200570 trace = '\n'.join(trace)
571
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200572 if not cancel:
Victor Stinnerde10f402011-04-08 12:57:06 +0200573 count = loops
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200574 if repeat:
Victor Stinnerde10f402011-04-08 12:57:06 +0200575 count *= 2
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700576 header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+ \(most recent call first\):\n' % timeout_str
Victor Stinner95bb7142015-03-12 15:32:03 +0100577 regex = expected_traceback(17, 26, header, min_count=count)
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200578 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200579 else:
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200580 self.assertEqual(trace, '')
Victor Stinner05585cb2011-03-31 13:29:56 +0200581 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200582
Georg Brandldeb92b52012-09-22 08:58:55 +0200583 def test_dump_traceback_later(self):
584 self.check_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200585
Georg Brandldeb92b52012-09-22 08:58:55 +0200586 def test_dump_traceback_later_repeat(self):
587 self.check_dump_traceback_later(repeat=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200588
Georg Brandldeb92b52012-09-22 08:58:55 +0200589 def test_dump_traceback_later_cancel(self):
590 self.check_dump_traceback_later(cancel=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200591
Georg Brandldeb92b52012-09-22 08:58:55 +0200592 def test_dump_traceback_later_file(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100593 with temporary_filename() as filename:
594 self.check_dump_traceback_later(filename=filename)
595
Victor Stinnerff2a6612015-03-13 11:01:30 +0100596 @unittest.skipIf(sys.platform == "win32",
597 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100598 def test_dump_traceback_later_fd(self):
599 with tempfile.TemporaryFile('wb+') as fp:
600 self.check_dump_traceback_later(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200601
Georg Brandldeb92b52012-09-22 08:58:55 +0200602 def test_dump_traceback_later_twice(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100603 self.check_dump_traceback_later(loops=2)
Victor Stinnerde10f402011-04-08 12:57:06 +0200604
Victor Stinner024e37a2011-03-31 01:31:06 +0200605 @unittest.skipIf(not hasattr(faulthandler, "register"),
606 "need faulthandler.register")
Victor Stinnera01ca122011-04-01 12:56:17 +0200607 def check_register(self, filename=False, all_threads=False,
Victor Stinner95bb7142015-03-12 15:32:03 +0100608 unregister=False, chain=False, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200609 """
610 Register a handler displaying the traceback on a user signal. Raise the
611 signal and check the written traceback.
612
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200613 If chain is True, check that the previous signal handler is called.
614
Victor Stinner024e37a2011-03-31 01:31:06 +0200615 Raise an error if the output doesn't match the expected format.
616 """
Victor Stinnera01ca122011-04-01 12:56:17 +0200617 signum = signal.SIGUSR1
Victor Stinner024e37a2011-03-31 01:31:06 +0200618 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200619 import faulthandler
620 import os
621 import signal
622 import sys
Victor Stinner024e37a2011-03-31 01:31:06 +0200623
Victor Stinner95bb7142015-03-12 15:32:03 +0100624 all_threads = {all_threads}
625 signum = {signum}
626 unregister = {unregister}
627 chain = {chain}
628 filename = {filename!r}
629 fd = {fd}
630
Victor Stinner6d201682014-08-10 19:50:08 +0200631 def func(signum):
632 os.kill(os.getpid(), signum)
Victor Stinner024e37a2011-03-31 01:31:06 +0200633
Victor Stinner6d201682014-08-10 19:50:08 +0200634 def handler(signum, frame):
635 handler.called = True
636 handler.called = False
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200637
Victor Stinner95bb7142015-03-12 15:32:03 +0100638 if filename:
639 file = open(filename, "wb")
640 elif fd is not None:
641 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200642 else:
643 file = None
644 if chain:
645 signal.signal(signum, handler)
646 faulthandler.register(signum, file=file,
Victor Stinner95bb7142015-03-12 15:32:03 +0100647 all_threads=all_threads, chain={chain})
Victor Stinner6d201682014-08-10 19:50:08 +0200648 if unregister:
649 faulthandler.unregister(signum)
650 func(signum)
651 if chain and not handler.called:
652 if file is not None:
653 output = file
654 else:
655 output = sys.stderr
656 print("Error: signal handler not called!", file=output)
657 exitcode = 1
Victor Stinner95bb7142015-03-12 15:32:03 +0100658 else:
659 exitcode = 0
660 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200661 file.close()
662 sys.exit(exitcode)
663 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200664 code = code.format(
Victor Stinner024e37a2011-03-31 01:31:06 +0200665 all_threads=all_threads,
Victor Stinnera01ca122011-04-01 12:56:17 +0200666 signum=signum,
667 unregister=unregister,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200668 chain=chain,
Victor Stinner95bb7142015-03-12 15:32:03 +0100669 filename=filename,
670 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200671 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200672 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200673 trace = '\n'.join(trace)
Victor Stinnera01ca122011-04-01 12:56:17 +0200674 if not unregister:
675 if all_threads:
R David Murray44b548d2016-09-08 13:59:53 -0400676 regex = r'Current thread 0x[0-9a-f]+ \(most recent call first\):\n'
Victor Stinnera01ca122011-04-01 12:56:17 +0200677 else:
R David Murray44b548d2016-09-08 13:59:53 -0400678 regex = r'Stack \(most recent call first\):\n'
Victor Stinner95bb7142015-03-12 15:32:03 +0100679 regex = expected_traceback(14, 32, regex)
Victor Stinnera01ca122011-04-01 12:56:17 +0200680 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200681 else:
Victor Stinnera01ca122011-04-01 12:56:17 +0200682 self.assertEqual(trace, '')
683 if unregister:
684 self.assertNotEqual(exitcode, 0)
685 else:
686 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200687
688 def test_register(self):
689 self.check_register()
690
Victor Stinnera01ca122011-04-01 12:56:17 +0200691 def test_unregister(self):
692 self.check_register(unregister=True)
693
Victor Stinner024e37a2011-03-31 01:31:06 +0200694 def test_register_file(self):
695 with temporary_filename() as filename:
696 self.check_register(filename=filename)
697
Victor Stinnerff2a6612015-03-13 11:01:30 +0100698 @unittest.skipIf(sys.platform == "win32",
699 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100700 def test_register_fd(self):
701 with tempfile.TemporaryFile('wb+') as fp:
702 self.check_register(fd=fp.fileno())
703
Victor Stinner024e37a2011-03-31 01:31:06 +0200704 def test_register_threads(self):
705 self.check_register(all_threads=True)
706
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200707 def test_register_chain(self):
708 self.check_register(chain=True)
709
Victor Stinnere2d66902014-05-14 17:15:50 +0200710 @contextmanager
711 def check_stderr_none(self):
712 stderr = sys.stderr
713 try:
714 sys.stderr = None
715 with self.assertRaises(RuntimeError) as cm:
716 yield
717 self.assertEqual(str(cm.exception), "sys.stderr is None")
718 finally:
719 sys.stderr = stderr
720
721 def test_stderr_None(self):
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +0300722 # Issue #21497: provide a helpful error if sys.stderr is None,
Victor Stinnere2d66902014-05-14 17:15:50 +0200723 # instead of just an attribute error: "None has no attribute fileno".
724 with self.check_stderr_none():
725 faulthandler.enable()
726 with self.check_stderr_none():
727 faulthandler.dump_traceback()
728 if hasattr(faulthandler, 'dump_traceback_later'):
729 with self.check_stderr_none():
730 faulthandler.dump_traceback_later(1e-3)
731 if hasattr(faulthandler, "register"):
732 with self.check_stderr_none():
733 faulthandler.register(signal.SIGUSR1)
734
Victor Stinner404cdc52016-03-23 10:39:17 +0100735 @unittest.skipUnless(MS_WINDOWS, 'specific to Windows')
736 def test_raise_exception(self):
737 for exc, name in (
738 ('EXCEPTION_ACCESS_VIOLATION', 'access violation'),
739 ('EXCEPTION_INT_DIVIDE_BY_ZERO', 'int divide by zero'),
740 ('EXCEPTION_STACK_OVERFLOW', 'stack overflow'),
741 ):
Eric V. Smith451d0e32016-09-09 21:56:20 -0400742 self.check_windows_exception(f"""
Victor Stinner404cdc52016-03-23 10:39:17 +0100743 import faulthandler
744 faulthandler.enable()
745 faulthandler._raise_exception(faulthandler._{exc})
Eric V. Smith451d0e32016-09-09 21:56:20 -0400746 """,
Victor Stinner404cdc52016-03-23 10:39:17 +0100747 3,
748 name)
749
Victor Stinner46c2b812017-04-21 18:06:13 +0200750 @unittest.skipUnless(MS_WINDOWS, 'specific to Windows')
Victor Stinner6e3d6b52017-10-09 09:52:32 -0700751 def test_ignore_exception(self):
752 for exc_code in (
753 0xE06D7363, # MSC exception ("Emsc")
754 0xE0434352, # COM Callable Runtime exception ("ECCR")
755 ):
756 code = f"""
757 import faulthandler
758 faulthandler.enable()
759 faulthandler._raise_exception({exc_code})
760 """
761 code = dedent(code)
762 output, exitcode = self.get_output(code)
763 self.assertEqual(output, [])
764 self.assertEqual(exitcode, exc_code)
765
766 @unittest.skipUnless(MS_WINDOWS, 'specific to Windows')
Steve Dowere6a23c82017-06-05 15:54:15 -0700767 def test_raise_nonfatal_exception(self):
768 # These exceptions are not strictly errors. Letting
769 # faulthandler display the traceback when they are
770 # raised is likely to result in noise. However, they
771 # may still terminate the process if there is no
772 # handler installed for them (which there typically
773 # is, e.g. for debug messages).
774 for exc in (
775 0x00000000,
776 0x34567890,
777 0x40000000,
778 0x40001000,
779 0x70000000,
780 0x7FFFFFFF,
781 ):
782 output, exitcode = self.get_output(f"""
783 import faulthandler
784 faulthandler.enable()
785 faulthandler._raise_exception(0x{exc:x})
786 """
787 )
788 self.assertEqual(output, [])
Victor Stinner6a1d84e2017-06-06 19:40:41 +0200789 # On Windows older than 7 SP1, the actual exception code has
790 # bit 29 cleared.
791 self.assertIn(exitcode,
792 (exc, exc & ~0x10000000))
Steve Dowere6a23c82017-06-05 15:54:15 -0700793
794 @unittest.skipUnless(MS_WINDOWS, 'specific to Windows')
Victor Stinner46c2b812017-04-21 18:06:13 +0200795 def test_disable_windows_exc_handler(self):
796 code = dedent("""
797 import faulthandler
798 faulthandler.enable()
799 faulthandler.disable()
800 code = faulthandler._EXCEPTION_ACCESS_VIOLATION
801 faulthandler._raise_exception(code)
802 """)
803 output, exitcode = self.get_output(code)
804 self.assertEqual(output, [])
805 self.assertEqual(exitcode, 0xC0000005)
Victor Stinner404cdc52016-03-23 10:39:17 +0100806
Victor Stinner024e37a2011-03-31 01:31:06 +0200807
Victor Stinner024e37a2011-03-31 01:31:06 +0200808if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400809 unittest.main()