blob: 889e6414e39aa8bc670c00c69badfa9df23b8d1a [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
Xavier de Gaye6c5f2192016-11-13 20:46:46 +01009from test.support import script_helper, is_android, requires_android_level
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
32@contextmanager
33def temporary_filename():
34 filename = tempfile.mktemp()
35 try:
36 yield filename
37 finally:
38 support.unlink(filename)
39
Xavier de Gaye6c5f2192016-11-13 20:46:46 +010040def requires_raise(test):
41 return (test if not is_android else
Xavier de Gaye524eac02016-11-13 21:14:03 +010042 requires_android_level(24, 'raise() is buggy')(test))
Xavier de Gaye6c5f2192016-11-13 20:46:46 +010043
Victor Stinner024e37a2011-03-31 01:31:06 +020044class FaultHandlerTests(unittest.TestCase):
Victor Stinner95bb7142015-03-12 15:32:03 +010045 def get_output(self, code, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020046 """
47 Run the specified code in Python (in a new child process) and read the
48 output from the standard error or from a file (if filename is set).
49 Return the output lines as a list.
50
51 Strip the reference count from the standard error for Python debug
52 build, and replace "Current thread 0x00007f8d8fbd9700" by "Current
53 thread XXX".
54 """
Victor Stinner6d201682014-08-10 19:50:08 +020055 code = dedent(code).strip()
Victor Stinner95bb7142015-03-12 15:32:03 +010056 pass_fds = []
57 if fd is not None:
58 pass_fds.append(fd)
Antoine Pitrou77e904e2013-10-08 23:04:32 +020059 with support.SuppressCrashReport():
Victor Stinner95bb7142015-03-12 15:32:03 +010060 process = script_helper.spawn_python('-c', code, pass_fds=pass_fds)
Victor Stinner861d9ab2016-03-16 22:45:24 +010061 with process:
62 stdout, stderr = process.communicate()
63 exitcode = process.wait()
Victor Stinner19402332011-03-31 18:15:52 +020064 output = support.strip_python_stderr(stdout)
65 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020066 if filename:
Victor Stinner19402332011-03-31 18:15:52 +020067 self.assertEqual(output, '')
Victor Stinner024e37a2011-03-31 01:31:06 +020068 with open(filename, "rb") as fp:
69 output = fp.read()
Victor Stinner19402332011-03-31 18:15:52 +020070 output = output.decode('ascii', 'backslashreplace')
Victor Stinner95bb7142015-03-12 15:32:03 +010071 elif fd is not None:
72 self.assertEqual(output, '')
73 os.lseek(fd, os.SEEK_SET, 0)
74 with open(fd, "rb", closefd=False) as fp:
75 output = fp.read()
76 output = output.decode('ascii', 'backslashreplace')
Victor Stinner05585cb2011-03-31 13:29:56 +020077 return output.splitlines(), exitcode
Victor Stinner024e37a2011-03-31 01:31:06 +020078
Victor Stinner404cdc52016-03-23 10:39:17 +010079 def check_error(self, code, line_number, fatal_error, *,
80 filename=None, all_threads=True, other_regex=None,
81 fd=None, know_current_thread=True):
Victor Stinner024e37a2011-03-31 01:31:06 +020082 """
83 Check that the fault handler for fatal errors is enabled and check the
84 traceback from the child process output.
85
86 Raise an error if the output doesn't match the expected format.
87 """
88 if all_threads:
Victor Stinner861d9ab2016-03-16 22:45:24 +010089 if know_current_thread:
90 header = 'Current thread 0x[0-9a-f]+'
91 else:
92 header = 'Thread 0x[0-9a-f]+'
Victor Stinner024e37a2011-03-31 01:31:06 +020093 else:
Victor Stinner861d9ab2016-03-16 22:45:24 +010094 header = 'Stack'
R David Murray44b548d2016-09-08 13:59:53 -040095 regex = r"""
Victor Stinner404cdc52016-03-23 10:39:17 +010096 ^{fatal_error}
Victor Stinner024e37a2011-03-31 01:31:06 +020097
Victor Stinner861d9ab2016-03-16 22:45:24 +010098 {header} \(most recent call first\):
Victor Stinner6d201682014-08-10 19:50:08 +020099 File "<string>", line {lineno} in <module>
100 """
101 regex = dedent(regex.format(
Victor Stinner024e37a2011-03-31 01:31:06 +0200102 lineno=line_number,
Victor Stinner404cdc52016-03-23 10:39:17 +0100103 fatal_error=fatal_error,
Victor Stinner861d9ab2016-03-16 22:45:24 +0100104 header=header)).strip()
Victor Stinnerf0480752011-03-31 11:34:08 +0200105 if other_regex:
106 regex += '|' + other_regex
Victor Stinner95bb7142015-03-12 15:32:03 +0100107 output, exitcode = self.get_output(code, filename=filename, fd=fd)
Victor Stinner024e37a2011-03-31 01:31:06 +0200108 output = '\n'.join(output)
109 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200110 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200111
Victor Stinner404cdc52016-03-23 10:39:17 +0100112 def check_fatal_error(self, code, line_number, name_regex, **kw):
113 fatal_error = 'Fatal Python error: %s' % name_regex
114 self.check_error(code, line_number, fatal_error, **kw)
115
116 def check_windows_exception(self, code, line_number, name_regex, **kw):
Victor Stinner412a5e72016-03-23 14:44:14 +0100117 fatal_error = 'Windows fatal exception: %s' % name_regex
Victor Stinner404cdc52016-03-23 10:39:17 +0100118 self.check_error(code, line_number, fatal_error, **kw)
119
Victor Stinner330426c2013-07-03 22:29:42 +0200120 @unittest.skipIf(sys.platform.startswith('aix'),
121 "the first page of memory is a mapped read-only on AIX")
Victor Stinner024e37a2011-03-31 01:31:06 +0200122 def test_read_null(self):
Victor Stinner404cdc52016-03-23 10:39:17 +0100123 if not MS_WINDOWS:
124 self.check_fatal_error("""
125 import faulthandler
126 faulthandler.enable()
127 faulthandler._read_null()
128 """,
129 3,
130 # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion
131 '(?:Segmentation fault'
132 '|Bus error'
133 '|Illegal instruction)')
134 else:
135 self.check_windows_exception("""
136 import faulthandler
137 faulthandler.enable()
138 faulthandler._read_null()
139 """,
140 3,
141 'access violation')
Victor Stinner024e37a2011-03-31 01:31:06 +0200142
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100143 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200144 def test_sigsegv(self):
145 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200146 import faulthandler
147 faulthandler.enable()
148 faulthandler._sigsegv()
149 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200150 3,
151 'Segmentation fault')
152
Victor Stinner861d9ab2016-03-16 22:45:24 +0100153 def test_fatal_error_c_thread(self):
154 self.check_fatal_error("""
155 import faulthandler
156 faulthandler.enable()
157 faulthandler._fatal_error_c_thread()
158 """,
159 3,
160 'in new thread',
161 know_current_thread=False)
162
Victor Stinnerd727e232011-04-01 12:13:55 +0200163 def test_sigabrt(self):
164 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200165 import faulthandler
166 faulthandler.enable()
167 faulthandler._sigabrt()
168 """,
Victor Stinnerd727e232011-04-01 12:13:55 +0200169 3,
170 'Aborted')
171
Victor Stinner024e37a2011-03-31 01:31:06 +0200172 @unittest.skipIf(sys.platform == 'win32',
173 "SIGFPE cannot be caught on Windows")
174 def test_sigfpe(self):
175 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200176 import faulthandler
177 faulthandler.enable()
178 faulthandler._sigfpe()
179 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200180 3,
181 'Floating point exception')
182
Victor Stinner56e8c292014-07-21 12:30:22 +0200183 @unittest.skipIf(_testcapi is None, 'need _testcapi')
184 @unittest.skipUnless(hasattr(signal, 'SIGBUS'), 'need signal.SIGBUS')
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100185 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200186 def test_sigbus(self):
187 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200188 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200189 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200190 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200191
Victor Stinner6d201682014-08-10 19:50:08 +0200192 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200193 _testcapi.raise_signal(signal.SIGBUS)
Victor Stinner6d201682014-08-10 19:50:08 +0200194 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200195 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200196 'Bus error')
197
Victor Stinner56e8c292014-07-21 12:30:22 +0200198 @unittest.skipIf(_testcapi is None, 'need _testcapi')
199 @unittest.skipUnless(hasattr(signal, 'SIGILL'), 'need signal.SIGILL')
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100200 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200201 def test_sigill(self):
202 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200203 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200204 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200205 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200206
Victor Stinner6d201682014-08-10 19:50:08 +0200207 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200208 _testcapi.raise_signal(signal.SIGILL)
Victor Stinner6d201682014-08-10 19:50:08 +0200209 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200210 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200211 'Illegal instruction')
212
213 def test_fatal_error(self):
214 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200215 import faulthandler
216 faulthandler._fatal_error(b'xyz')
217 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200218 2,
219 'xyz')
220
Victor Stinner57003f82016-03-15 17:23:35 +0100221 def test_fatal_error_without_gil(self):
222 self.check_fatal_error("""
223 import faulthandler
224 faulthandler._fatal_error(b'xyz', True)
225 """,
226 2,
227 'xyz')
228
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200229 @unittest.skipIf(sys.platform.startswith('openbsd'),
Charles-François Natali3391e642011-09-01 23:08:21 +0200230 "Issue #12868: sigaltstack() doesn't work on "
231 "OpenBSD if Python is compiled with pthread")
Victor Stinner024e37a2011-03-31 01:31:06 +0200232 @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'),
233 'need faulthandler._stack_overflow()')
234 def test_stack_overflow(self):
235 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200236 import faulthandler
237 faulthandler.enable()
238 faulthandler._stack_overflow()
239 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200240 3,
Victor Stinnerf0480752011-03-31 11:34:08 +0200241 '(?:Segmentation fault|Bus error)',
242 other_regex='unable to raise a stack overflow')
Victor Stinner024e37a2011-03-31 01:31:06 +0200243
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100244 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200245 def test_gil_released(self):
246 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200247 import faulthandler
248 faulthandler.enable()
Victor Stinner50838282014-09-30 13:54:14 +0200249 faulthandler._sigsegv(True)
Victor Stinner6d201682014-08-10 19:50:08 +0200250 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200251 3,
Victor Stinner50838282014-09-30 13:54:14 +0200252 'Segmentation fault')
Victor Stinner024e37a2011-03-31 01:31:06 +0200253
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100254 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200255 def test_enable_file(self):
256 with temporary_filename() as filename:
257 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200258 import faulthandler
259 output = open({filename}, 'wb')
260 faulthandler.enable(output)
261 faulthandler._sigsegv()
262 """.format(filename=repr(filename)),
Victor Stinner024e37a2011-03-31 01:31:06 +0200263 4,
Victor Stinner56785392013-06-17 23:37:59 +0200264 'Segmentation fault',
Victor Stinner024e37a2011-03-31 01:31:06 +0200265 filename=filename)
266
Victor Stinnerff2a6612015-03-13 11:01:30 +0100267 @unittest.skipIf(sys.platform == "win32",
268 "subprocess doesn't support pass_fds on Windows")
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100269 @requires_raise
Victor Stinner95bb7142015-03-12 15:32:03 +0100270 def test_enable_fd(self):
271 with tempfile.TemporaryFile('wb+') as fp:
272 fd = fp.fileno()
273 self.check_fatal_error("""
274 import faulthandler
275 import sys
276 faulthandler.enable(%s)
277 faulthandler._sigsegv()
278 """ % fd,
279 4,
280 'Segmentation fault',
281 fd=fd)
282
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100283 @requires_raise
Victor Stinner7bba62f2011-05-07 12:43:00 +0200284 def test_enable_single_thread(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200285 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200286 import faulthandler
287 faulthandler.enable(all_threads=False)
288 faulthandler._sigsegv()
289 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200290 3,
Victor Stinner56785392013-06-17 23:37:59 +0200291 'Segmentation fault',
Victor Stinner7bba62f2011-05-07 12:43:00 +0200292 all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200293
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100294 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200295 def test_disable(self):
296 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200297 import faulthandler
298 faulthandler.enable()
299 faulthandler.disable()
300 faulthandler._sigsegv()
301 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200302 not_expected = 'Fatal Python error'
Antoine Pitrou77e904e2013-10-08 23:04:32 +0200303 stderr, exitcode = self.get_output(code)
Victor Stinner29001c82014-09-25 00:38:48 +0200304 stderr = '\n'.join(stderr)
Victor Stinner024e37a2011-03-31 01:31:06 +0200305 self.assertTrue(not_expected not in stderr,
306 "%r is present in %r" % (not_expected, stderr))
Victor Stinner05585cb2011-03-31 13:29:56 +0200307 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200308
309 def test_is_enabled(self):
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200310 orig_stderr = sys.stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200311 try:
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200312 # regrtest may replace sys.stderr by io.StringIO object, but
313 # faulthandler.enable() requires that sys.stderr has a fileno()
314 # method
Victor Stinner72488502011-06-29 23:24:31 +0200315 sys.stderr = sys.__stderr__
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200316
317 was_enabled = faulthandler.is_enabled()
318 try:
Victor Stinner024e37a2011-03-31 01:31:06 +0200319 faulthandler.enable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200320 self.assertTrue(faulthandler.is_enabled())
Victor Stinner024e37a2011-03-31 01:31:06 +0200321 faulthandler.disable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200322 self.assertFalse(faulthandler.is_enabled())
323 finally:
324 if was_enabled:
325 faulthandler.enable()
326 else:
327 faulthandler.disable()
328 finally:
329 sys.stderr = orig_stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200330
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200331 def test_disabled_by_default(self):
332 # By default, the module should be disabled
333 code = "import faulthandler; print(faulthandler.is_enabled())"
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800334 args = filter(None, (sys.executable,
335 "-E" if sys.flags.ignore_environment else "",
336 "-c", code))
337 env = os.environ.copy()
338 env.pop("PYTHONFAULTHANDLER", None)
339 # don't use assert_python_ok() because it always enables faulthandler
340 output = subprocess.check_output(args, env=env)
Victor Stinner88983502013-09-08 11:36:23 +0200341 self.assertEqual(output.rstrip(), b"False")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200342
343 def test_sys_xoptions(self):
344 # Test python -X faulthandler
345 code = "import faulthandler; print(faulthandler.is_enabled())"
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800346 args = filter(None, (sys.executable,
347 "-E" if sys.flags.ignore_environment else "",
348 "-X", "faulthandler", "-c", code))
349 env = os.environ.copy()
350 env.pop("PYTHONFAULTHANDLER", None)
351 # don't use assert_python_ok() because it always enables faulthandler
352 output = subprocess.check_output(args, env=env)
Victor Stinner88983502013-09-08 11:36:23 +0200353 self.assertEqual(output.rstrip(), b"True")
354
355 def test_env_var(self):
356 # empty env var
357 code = "import faulthandler; print(faulthandler.is_enabled())"
358 args = (sys.executable, "-c", code)
359 env = os.environ.copy()
360 env['PYTHONFAULTHANDLER'] = ''
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800361 # don't use assert_python_ok() because it always enables faulthandler
Victor Stinner88983502013-09-08 11:36:23 +0200362 output = subprocess.check_output(args, env=env)
363 self.assertEqual(output.rstrip(), b"False")
364
365 # non-empty env var
366 env = os.environ.copy()
367 env['PYTHONFAULTHANDLER'] = '1'
368 output = subprocess.check_output(args, env=env)
369 self.assertEqual(output.rstrip(), b"True")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200370
Victor Stinner95bb7142015-03-12 15:32:03 +0100371 def check_dump_traceback(self, *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200372 """
373 Explicitly call dump_traceback() function and check its output.
374 Raise an error if the output doesn't match the expected format.
375 """
376 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200377 import faulthandler
Victor Stinner024e37a2011-03-31 01:31:06 +0200378
Victor Stinner95bb7142015-03-12 15:32:03 +0100379 filename = {filename!r}
380 fd = {fd}
381
Victor Stinner6d201682014-08-10 19:50:08 +0200382 def funcB():
Victor Stinner95bb7142015-03-12 15:32:03 +0100383 if filename:
384 with open(filename, "wb") as fp:
Victor Stinner6d201682014-08-10 19:50:08 +0200385 faulthandler.dump_traceback(fp, all_threads=False)
Victor Stinner95bb7142015-03-12 15:32:03 +0100386 elif fd is not None:
387 faulthandler.dump_traceback(fd,
388 all_threads=False)
Victor Stinner6d201682014-08-10 19:50:08 +0200389 else:
390 faulthandler.dump_traceback(all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200391
Victor Stinner6d201682014-08-10 19:50:08 +0200392 def funcA():
393 funcB()
Victor Stinner024e37a2011-03-31 01:31:06 +0200394
Victor Stinner6d201682014-08-10 19:50:08 +0200395 funcA()
396 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200397 code = code.format(
Victor Stinner95bb7142015-03-12 15:32:03 +0100398 filename=filename,
399 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200400 )
401 if filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100402 lineno = 9
403 elif fd is not None:
404 lineno = 12
Victor Stinner024e37a2011-03-31 01:31:06 +0200405 else:
Victor Stinner95bb7142015-03-12 15:32:03 +0100406 lineno = 14
Victor Stinner024e37a2011-03-31 01:31:06 +0200407 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700408 'Stack (most recent call first):',
Victor Stinner024e37a2011-03-31 01:31:06 +0200409 ' File "<string>", line %s in funcB' % lineno,
Victor Stinner95bb7142015-03-12 15:32:03 +0100410 ' File "<string>", line 17 in funcA',
411 ' File "<string>", line 19 in <module>'
Victor Stinner024e37a2011-03-31 01:31:06 +0200412 ]
Victor Stinner95bb7142015-03-12 15:32:03 +0100413 trace, exitcode = self.get_output(code, filename, fd)
Victor Stinner024e37a2011-03-31 01:31:06 +0200414 self.assertEqual(trace, expected)
Victor Stinner05585cb2011-03-31 13:29:56 +0200415 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200416
417 def test_dump_traceback(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100418 self.check_dump_traceback()
Victor Stinner19402332011-03-31 18:15:52 +0200419
420 def test_dump_traceback_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200421 with temporary_filename() as filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100422 self.check_dump_traceback(filename=filename)
423
Victor Stinnerff2a6612015-03-13 11:01:30 +0100424 @unittest.skipIf(sys.platform == "win32",
425 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100426 def test_dump_traceback_fd(self):
427 with tempfile.TemporaryFile('wb+') as fp:
428 self.check_dump_traceback(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200429
Victor Stinner53386d82012-08-01 19:45:34 +0200430 def test_truncate(self):
431 maxlen = 500
432 func_name = 'x' * (maxlen + 50)
433 truncated = 'x' * maxlen + '...'
434 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200435 import faulthandler
Victor Stinner53386d82012-08-01 19:45:34 +0200436
Victor Stinner6d201682014-08-10 19:50:08 +0200437 def {func_name}():
438 faulthandler.dump_traceback(all_threads=False)
Victor Stinner53386d82012-08-01 19:45:34 +0200439
Victor Stinner6d201682014-08-10 19:50:08 +0200440 {func_name}()
441 """
Victor Stinner53386d82012-08-01 19:45:34 +0200442 code = code.format(
443 func_name=func_name,
444 )
445 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700446 'Stack (most recent call first):',
Victor Stinner53386d82012-08-01 19:45:34 +0200447 ' File "<string>", line 4 in %s' % truncated,
448 ' File "<string>", line 6 in <module>'
449 ]
450 trace, exitcode = self.get_output(code)
451 self.assertEqual(trace, expected)
452 self.assertEqual(exitcode, 0)
453
Victor Stinner024e37a2011-03-31 01:31:06 +0200454 def check_dump_traceback_threads(self, filename):
455 """
456 Call explicitly dump_traceback(all_threads=True) and check the output.
457 Raise an error if the output doesn't match the expected format.
458 """
459 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200460 import faulthandler
461 from threading import Thread, Event
462 import time
Victor Stinner024e37a2011-03-31 01:31:06 +0200463
Victor Stinner6d201682014-08-10 19:50:08 +0200464 def dump():
465 if {filename}:
466 with open({filename}, "wb") as fp:
467 faulthandler.dump_traceback(fp, all_threads=True)
468 else:
469 faulthandler.dump_traceback(all_threads=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200470
Victor Stinner6d201682014-08-10 19:50:08 +0200471 class Waiter(Thread):
472 # avoid blocking if the main thread raises an exception.
473 daemon = True
Victor Stinner024e37a2011-03-31 01:31:06 +0200474
Victor Stinner6d201682014-08-10 19:50:08 +0200475 def __init__(self):
476 Thread.__init__(self)
477 self.running = Event()
478 self.stop = Event()
Victor Stinner024e37a2011-03-31 01:31:06 +0200479
Victor Stinner6d201682014-08-10 19:50:08 +0200480 def run(self):
481 self.running.set()
482 self.stop.wait()
Victor Stinner024e37a2011-03-31 01:31:06 +0200483
Victor Stinner6d201682014-08-10 19:50:08 +0200484 waiter = Waiter()
485 waiter.start()
486 waiter.running.wait()
487 dump()
488 waiter.stop.set()
489 waiter.join()
490 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200491 code = code.format(filename=repr(filename))
Victor Stinner05585cb2011-03-31 13:29:56 +0200492 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200493 output = '\n'.join(output)
494 if filename:
495 lineno = 8
496 else:
497 lineno = 10
R David Murray44b548d2016-09-08 13:59:53 -0400498 regex = r"""
Victor Stinner6d201682014-08-10 19:50:08 +0200499 ^Thread 0x[0-9a-f]+ \(most recent call first\):
500 (?: File ".*threading.py", line [0-9]+ in [_a-z]+
501 ){{1,3}} File "<string>", line 23 in run
502 File ".*threading.py", line [0-9]+ in _bootstrap_inner
503 File ".*threading.py", line [0-9]+ in _bootstrap
Victor Stinner024e37a2011-03-31 01:31:06 +0200504
Victor Stinner861d9ab2016-03-16 22:45:24 +0100505 Current thread 0x[0-9a-f]+ \(most recent call first\):
Victor Stinner6d201682014-08-10 19:50:08 +0200506 File "<string>", line {lineno} in dump
507 File "<string>", line 28 in <module>$
508 """
509 regex = dedent(regex.format(lineno=lineno)).strip()
Victor Stinner024e37a2011-03-31 01:31:06 +0200510 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200511 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200512
513 def test_dump_traceback_threads(self):
514 self.check_dump_traceback_threads(None)
Victor Stinner19402332011-03-31 18:15:52 +0200515
516 def test_dump_traceback_threads_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200517 with temporary_filename() as filename:
518 self.check_dump_traceback_threads(filename)
519
Victor Stinner95bb7142015-03-12 15:32:03 +0100520 @unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'),
521 'need faulthandler.dump_traceback_later()')
522 def check_dump_traceback_later(self, repeat=False, cancel=False, loops=1,
523 *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200524 """
525 Check how many times the traceback is written in timeout x 2.5 seconds,
526 or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending
527 on repeat and cancel options.
528
529 Raise an error if the output doesn't match the expect format.
530 """
Victor Stinnerc790a532011-04-08 13:39:59 +0200531 timeout_str = str(datetime.timedelta(seconds=TIMEOUT))
Victor Stinner024e37a2011-03-31 01:31:06 +0200532 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200533 import faulthandler
534 import time
Victor Stinner95bb7142015-03-12 15:32:03 +0100535 import sys
536
537 timeout = {timeout}
538 repeat = {repeat}
539 cancel = {cancel}
540 loops = {loops}
541 filename = {filename!r}
542 fd = {fd}
Victor Stinner024e37a2011-03-31 01:31:06 +0200543
Victor Stinner6d201682014-08-10 19:50:08 +0200544 def func(timeout, repeat, cancel, file, loops):
545 for loop in range(loops):
546 faulthandler.dump_traceback_later(timeout, repeat=repeat, file=file)
547 if cancel:
548 faulthandler.cancel_dump_traceback_later()
549 time.sleep(timeout * 5)
550 faulthandler.cancel_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200551
Victor Stinner95bb7142015-03-12 15:32:03 +0100552 if filename:
553 file = open(filename, "wb")
554 elif fd is not None:
555 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200556 else:
557 file = None
558 func(timeout, repeat, cancel, file, loops)
Victor Stinner95bb7142015-03-12 15:32:03 +0100559 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200560 file.close()
561 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200562 code = code.format(
Victor Stinnerde10f402011-04-08 12:57:06 +0200563 timeout=TIMEOUT,
Victor Stinner024e37a2011-03-31 01:31:06 +0200564 repeat=repeat,
565 cancel=cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200566 loops=loops,
Victor Stinner95bb7142015-03-12 15:32:03 +0100567 filename=filename,
568 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200569 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200570 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200571 trace = '\n'.join(trace)
572
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200573 if not cancel:
Victor Stinnerde10f402011-04-08 12:57:06 +0200574 count = loops
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200575 if repeat:
Victor Stinnerde10f402011-04-08 12:57:06 +0200576 count *= 2
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700577 header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+ \(most recent call first\):\n' % timeout_str
Victor Stinner95bb7142015-03-12 15:32:03 +0100578 regex = expected_traceback(17, 26, header, min_count=count)
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200579 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200580 else:
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200581 self.assertEqual(trace, '')
Victor Stinner05585cb2011-03-31 13:29:56 +0200582 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200583
Georg Brandldeb92b52012-09-22 08:58:55 +0200584 def test_dump_traceback_later(self):
585 self.check_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200586
Georg Brandldeb92b52012-09-22 08:58:55 +0200587 def test_dump_traceback_later_repeat(self):
588 self.check_dump_traceback_later(repeat=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200589
Georg Brandldeb92b52012-09-22 08:58:55 +0200590 def test_dump_traceback_later_cancel(self):
591 self.check_dump_traceback_later(cancel=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200592
Georg Brandldeb92b52012-09-22 08:58:55 +0200593 def test_dump_traceback_later_file(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100594 with temporary_filename() as filename:
595 self.check_dump_traceback_later(filename=filename)
596
Victor Stinnerff2a6612015-03-13 11:01:30 +0100597 @unittest.skipIf(sys.platform == "win32",
598 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100599 def test_dump_traceback_later_fd(self):
600 with tempfile.TemporaryFile('wb+') as fp:
601 self.check_dump_traceback_later(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200602
Georg Brandldeb92b52012-09-22 08:58:55 +0200603 def test_dump_traceback_later_twice(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100604 self.check_dump_traceback_later(loops=2)
Victor Stinnerde10f402011-04-08 12:57:06 +0200605
Victor Stinner024e37a2011-03-31 01:31:06 +0200606 @unittest.skipIf(not hasattr(faulthandler, "register"),
607 "need faulthandler.register")
Victor Stinnera01ca122011-04-01 12:56:17 +0200608 def check_register(self, filename=False, all_threads=False,
Victor Stinner95bb7142015-03-12 15:32:03 +0100609 unregister=False, chain=False, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200610 """
611 Register a handler displaying the traceback on a user signal. Raise the
612 signal and check the written traceback.
613
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200614 If chain is True, check that the previous signal handler is called.
615
Victor Stinner024e37a2011-03-31 01:31:06 +0200616 Raise an error if the output doesn't match the expected format.
617 """
Victor Stinnera01ca122011-04-01 12:56:17 +0200618 signum = signal.SIGUSR1
Victor Stinner024e37a2011-03-31 01:31:06 +0200619 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200620 import faulthandler
621 import os
622 import signal
623 import sys
Victor Stinner024e37a2011-03-31 01:31:06 +0200624
Victor Stinner95bb7142015-03-12 15:32:03 +0100625 all_threads = {all_threads}
626 signum = {signum}
627 unregister = {unregister}
628 chain = {chain}
629 filename = {filename!r}
630 fd = {fd}
631
Victor Stinner6d201682014-08-10 19:50:08 +0200632 def func(signum):
633 os.kill(os.getpid(), signum)
Victor Stinner024e37a2011-03-31 01:31:06 +0200634
Victor Stinner6d201682014-08-10 19:50:08 +0200635 def handler(signum, frame):
636 handler.called = True
637 handler.called = False
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200638
Victor Stinner95bb7142015-03-12 15:32:03 +0100639 if filename:
640 file = open(filename, "wb")
641 elif fd is not None:
642 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200643 else:
644 file = None
645 if chain:
646 signal.signal(signum, handler)
647 faulthandler.register(signum, file=file,
Victor Stinner95bb7142015-03-12 15:32:03 +0100648 all_threads=all_threads, chain={chain})
Victor Stinner6d201682014-08-10 19:50:08 +0200649 if unregister:
650 faulthandler.unregister(signum)
651 func(signum)
652 if chain and not handler.called:
653 if file is not None:
654 output = file
655 else:
656 output = sys.stderr
657 print("Error: signal handler not called!", file=output)
658 exitcode = 1
Victor Stinner95bb7142015-03-12 15:32:03 +0100659 else:
660 exitcode = 0
661 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200662 file.close()
663 sys.exit(exitcode)
664 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200665 code = code.format(
Victor Stinner024e37a2011-03-31 01:31:06 +0200666 all_threads=all_threads,
Victor Stinnera01ca122011-04-01 12:56:17 +0200667 signum=signum,
668 unregister=unregister,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200669 chain=chain,
Victor Stinner95bb7142015-03-12 15:32:03 +0100670 filename=filename,
671 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200672 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200673 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200674 trace = '\n'.join(trace)
Victor Stinnera01ca122011-04-01 12:56:17 +0200675 if not unregister:
676 if all_threads:
R David Murray44b548d2016-09-08 13:59:53 -0400677 regex = r'Current thread 0x[0-9a-f]+ \(most recent call first\):\n'
Victor Stinnera01ca122011-04-01 12:56:17 +0200678 else:
R David Murray44b548d2016-09-08 13:59:53 -0400679 regex = r'Stack \(most recent call first\):\n'
Victor Stinner95bb7142015-03-12 15:32:03 +0100680 regex = expected_traceback(14, 32, regex)
Victor Stinnera01ca122011-04-01 12:56:17 +0200681 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200682 else:
Victor Stinnera01ca122011-04-01 12:56:17 +0200683 self.assertEqual(trace, '')
684 if unregister:
685 self.assertNotEqual(exitcode, 0)
686 else:
687 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200688
689 def test_register(self):
690 self.check_register()
691
Victor Stinnera01ca122011-04-01 12:56:17 +0200692 def test_unregister(self):
693 self.check_register(unregister=True)
694
Victor Stinner024e37a2011-03-31 01:31:06 +0200695 def test_register_file(self):
696 with temporary_filename() as filename:
697 self.check_register(filename=filename)
698
Victor Stinnerff2a6612015-03-13 11:01:30 +0100699 @unittest.skipIf(sys.platform == "win32",
700 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100701 def test_register_fd(self):
702 with tempfile.TemporaryFile('wb+') as fp:
703 self.check_register(fd=fp.fileno())
704
Victor Stinner024e37a2011-03-31 01:31:06 +0200705 def test_register_threads(self):
706 self.check_register(all_threads=True)
707
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200708 def test_register_chain(self):
709 self.check_register(chain=True)
710
Victor Stinnere2d66902014-05-14 17:15:50 +0200711 @contextmanager
712 def check_stderr_none(self):
713 stderr = sys.stderr
714 try:
715 sys.stderr = None
716 with self.assertRaises(RuntimeError) as cm:
717 yield
718 self.assertEqual(str(cm.exception), "sys.stderr is None")
719 finally:
720 sys.stderr = stderr
721
722 def test_stderr_None(self):
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +0300723 # Issue #21497: provide a helpful error if sys.stderr is None,
Victor Stinnere2d66902014-05-14 17:15:50 +0200724 # instead of just an attribute error: "None has no attribute fileno".
725 with self.check_stderr_none():
726 faulthandler.enable()
727 with self.check_stderr_none():
728 faulthandler.dump_traceback()
729 if hasattr(faulthandler, 'dump_traceback_later'):
730 with self.check_stderr_none():
731 faulthandler.dump_traceback_later(1e-3)
732 if hasattr(faulthandler, "register"):
733 with self.check_stderr_none():
734 faulthandler.register(signal.SIGUSR1)
735
Victor Stinner404cdc52016-03-23 10:39:17 +0100736 @unittest.skipUnless(MS_WINDOWS, 'specific to Windows')
737 def test_raise_exception(self):
738 for exc, name in (
739 ('EXCEPTION_ACCESS_VIOLATION', 'access violation'),
740 ('EXCEPTION_INT_DIVIDE_BY_ZERO', 'int divide by zero'),
741 ('EXCEPTION_STACK_OVERFLOW', 'stack overflow'),
742 ):
Eric V. Smith451d0e32016-09-09 21:56:20 -0400743 self.check_windows_exception(f"""
Victor Stinner404cdc52016-03-23 10:39:17 +0100744 import faulthandler
745 faulthandler.enable()
746 faulthandler._raise_exception(faulthandler._{exc})
Eric V. Smith451d0e32016-09-09 21:56:20 -0400747 """,
Victor Stinner404cdc52016-03-23 10:39:17 +0100748 3,
749 name)
750
Victor Stinner46c2b812017-04-21 18:06:13 +0200751 @unittest.skipUnless(MS_WINDOWS, 'specific to Windows')
Steve Dowere6a23c82017-06-05 15:54:15 -0700752 def test_raise_nonfatal_exception(self):
753 # These exceptions are not strictly errors. Letting
754 # faulthandler display the traceback when they are
755 # raised is likely to result in noise. However, they
756 # may still terminate the process if there is no
757 # handler installed for them (which there typically
758 # is, e.g. for debug messages).
759 for exc in (
760 0x00000000,
761 0x34567890,
762 0x40000000,
763 0x40001000,
764 0x70000000,
765 0x7FFFFFFF,
766 ):
767 output, exitcode = self.get_output(f"""
768 import faulthandler
769 faulthandler.enable()
770 faulthandler._raise_exception(0x{exc:x})
771 """
772 )
773 self.assertEqual(output, [])
Victor Stinner6a1d84e2017-06-06 19:40:41 +0200774 # On Windows older than 7 SP1, the actual exception code has
775 # bit 29 cleared.
776 self.assertIn(exitcode,
777 (exc, exc & ~0x10000000))
Steve Dowere6a23c82017-06-05 15:54:15 -0700778
779 @unittest.skipUnless(MS_WINDOWS, 'specific to Windows')
Victor Stinner46c2b812017-04-21 18:06:13 +0200780 def test_disable_windows_exc_handler(self):
781 code = dedent("""
782 import faulthandler
783 faulthandler.enable()
784 faulthandler.disable()
785 code = faulthandler._EXCEPTION_ACCESS_VIOLATION
786 faulthandler._raise_exception(code)
787 """)
788 output, exitcode = self.get_output(code)
789 self.assertEqual(output, [])
790 self.assertEqual(exitcode, 0xC0000005)
Victor Stinner404cdc52016-03-23 10:39:17 +0100791
Victor Stinner024e37a2011-03-31 01:31:06 +0200792
Victor Stinner024e37a2011-03-31 01:31:06 +0200793if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400794 unittest.main()