blob: 28dd5f4e98f9383b503ac91c38fb5b17c4b8a9f2 [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
11import unittest
Victor Stinner6d201682014-08-10 19:50:08 +020012from textwrap import dedent
Victor Stinner024e37a2011-03-31 01:31:06 +020013
Victor Stinnerff4cd882011-04-07 11:50:25 +020014try:
15 import threading
16 HAVE_THREADS = True
17except ImportError:
18 HAVE_THREADS = False
Victor Stinner56e8c292014-07-21 12:30:22 +020019try:
20 import _testcapi
21except ImportError:
22 _testcapi = None
Victor Stinnerff4cd882011-04-07 11:50:25 +020023
Victor Stinner44378d42011-04-01 15:37:12 +020024TIMEOUT = 0.5
Victor Stinner404cdc52016-03-23 10:39:17 +010025MS_WINDOWS = (os.name == 'nt')
Victor Stinner44378d42011-04-01 15:37:12 +020026
Victor Stinner301f3f02011-06-01 13:49:12 +020027def expected_traceback(lineno1, lineno2, header, min_count=1):
Victor Stinner024e37a2011-03-31 01:31:06 +020028 regex = header
Victor Stinner7ad24e92011-03-31 22:35:49 +020029 regex += ' File "<string>", line %s in func\n' % lineno1
30 regex += ' File "<string>", line %s in <module>' % lineno2
Victor Stinner301f3f02011-06-01 13:49:12 +020031 if 1 < min_count:
32 return '^' + (regex + '\n') * (min_count - 1) + regex
33 else:
34 return '^' + regex + '$'
Victor Stinner024e37a2011-03-31 01:31:06 +020035
36@contextmanager
37def temporary_filename():
38 filename = tempfile.mktemp()
39 try:
40 yield filename
41 finally:
42 support.unlink(filename)
43
Xavier de Gaye6c5f2192016-11-13 20:46:46 +010044def requires_raise(test):
45 return (test if not is_android else
Xavier de Gaye524eac02016-11-13 21:14:03 +010046 requires_android_level(24, 'raise() is buggy')(test))
Xavier de Gaye6c5f2192016-11-13 20:46:46 +010047
Victor Stinner024e37a2011-03-31 01:31:06 +020048class FaultHandlerTests(unittest.TestCase):
Victor Stinner95bb7142015-03-12 15:32:03 +010049 def get_output(self, code, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020050 """
51 Run the specified code in Python (in a new child process) and read the
52 output from the standard error or from a file (if filename is set).
53 Return the output lines as a list.
54
55 Strip the reference count from the standard error for Python debug
56 build, and replace "Current thread 0x00007f8d8fbd9700" by "Current
57 thread XXX".
58 """
Victor Stinner6d201682014-08-10 19:50:08 +020059 code = dedent(code).strip()
Victor Stinner95bb7142015-03-12 15:32:03 +010060 pass_fds = []
61 if fd is not None:
62 pass_fds.append(fd)
Antoine Pitrou77e904e2013-10-08 23:04:32 +020063 with support.SuppressCrashReport():
Victor Stinner95bb7142015-03-12 15:32:03 +010064 process = script_helper.spawn_python('-c', code, pass_fds=pass_fds)
Victor Stinner861d9ab2016-03-16 22:45:24 +010065 with process:
66 stdout, stderr = process.communicate()
67 exitcode = process.wait()
Victor Stinner19402332011-03-31 18:15:52 +020068 output = support.strip_python_stderr(stdout)
69 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020070 if filename:
Victor Stinner19402332011-03-31 18:15:52 +020071 self.assertEqual(output, '')
Victor Stinner024e37a2011-03-31 01:31:06 +020072 with open(filename, "rb") as fp:
73 output = fp.read()
Victor Stinner19402332011-03-31 18:15:52 +020074 output = output.decode('ascii', 'backslashreplace')
Victor Stinner95bb7142015-03-12 15:32:03 +010075 elif fd is not None:
76 self.assertEqual(output, '')
77 os.lseek(fd, os.SEEK_SET, 0)
78 with open(fd, "rb", closefd=False) as fp:
79 output = fp.read()
80 output = output.decode('ascii', 'backslashreplace')
Victor Stinner05585cb2011-03-31 13:29:56 +020081 return output.splitlines(), exitcode
Victor Stinner024e37a2011-03-31 01:31:06 +020082
Victor Stinner404cdc52016-03-23 10:39:17 +010083 def check_error(self, code, line_number, fatal_error, *,
84 filename=None, all_threads=True, other_regex=None,
85 fd=None, know_current_thread=True):
Victor Stinner024e37a2011-03-31 01:31:06 +020086 """
87 Check that the fault handler for fatal errors is enabled and check the
88 traceback from the child process output.
89
90 Raise an error if the output doesn't match the expected format.
91 """
92 if all_threads:
Victor Stinner861d9ab2016-03-16 22:45:24 +010093 if know_current_thread:
94 header = 'Current thread 0x[0-9a-f]+'
95 else:
96 header = 'Thread 0x[0-9a-f]+'
Victor Stinner024e37a2011-03-31 01:31:06 +020097 else:
Victor Stinner861d9ab2016-03-16 22:45:24 +010098 header = 'Stack'
R David Murray44b548d2016-09-08 13:59:53 -040099 regex = r"""
Victor Stinner404cdc52016-03-23 10:39:17 +0100100 ^{fatal_error}
Victor Stinner024e37a2011-03-31 01:31:06 +0200101
Victor Stinner861d9ab2016-03-16 22:45:24 +0100102 {header} \(most recent call first\):
Victor Stinner6d201682014-08-10 19:50:08 +0200103 File "<string>", line {lineno} in <module>
104 """
105 regex = dedent(regex.format(
Victor Stinner024e37a2011-03-31 01:31:06 +0200106 lineno=line_number,
Victor Stinner404cdc52016-03-23 10:39:17 +0100107 fatal_error=fatal_error,
Victor Stinner861d9ab2016-03-16 22:45:24 +0100108 header=header)).strip()
Victor Stinnerf0480752011-03-31 11:34:08 +0200109 if other_regex:
110 regex += '|' + other_regex
Victor Stinner95bb7142015-03-12 15:32:03 +0100111 output, exitcode = self.get_output(code, filename=filename, fd=fd)
Victor Stinner024e37a2011-03-31 01:31:06 +0200112 output = '\n'.join(output)
113 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200114 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200115
Victor Stinner404cdc52016-03-23 10:39:17 +0100116 def check_fatal_error(self, code, line_number, name_regex, **kw):
117 fatal_error = 'Fatal Python error: %s' % name_regex
118 self.check_error(code, line_number, fatal_error, **kw)
119
120 def check_windows_exception(self, code, line_number, name_regex, **kw):
Victor Stinner412a5e72016-03-23 14:44:14 +0100121 fatal_error = 'Windows fatal exception: %s' % name_regex
Victor Stinner404cdc52016-03-23 10:39:17 +0100122 self.check_error(code, line_number, fatal_error, **kw)
123
Victor Stinner330426c2013-07-03 22:29:42 +0200124 @unittest.skipIf(sys.platform.startswith('aix'),
125 "the first page of memory is a mapped read-only on AIX")
Victor Stinner024e37a2011-03-31 01:31:06 +0200126 def test_read_null(self):
Victor Stinner404cdc52016-03-23 10:39:17 +0100127 if not MS_WINDOWS:
128 self.check_fatal_error("""
129 import faulthandler
130 faulthandler.enable()
131 faulthandler._read_null()
132 """,
133 3,
134 # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion
135 '(?:Segmentation fault'
136 '|Bus error'
137 '|Illegal instruction)')
138 else:
139 self.check_windows_exception("""
140 import faulthandler
141 faulthandler.enable()
142 faulthandler._read_null()
143 """,
144 3,
145 'access violation')
Victor Stinner024e37a2011-03-31 01:31:06 +0200146
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100147 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200148 def test_sigsegv(self):
149 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200150 import faulthandler
151 faulthandler.enable()
152 faulthandler._sigsegv()
153 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200154 3,
155 'Segmentation fault')
156
Victor Stinner861d9ab2016-03-16 22:45:24 +0100157 @unittest.skipIf(not HAVE_THREADS, 'need threads')
158 def test_fatal_error_c_thread(self):
159 self.check_fatal_error("""
160 import faulthandler
161 faulthandler.enable()
162 faulthandler._fatal_error_c_thread()
163 """,
164 3,
165 'in new thread',
166 know_current_thread=False)
167
Victor Stinnerd727e232011-04-01 12:13:55 +0200168 def test_sigabrt(self):
169 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200170 import faulthandler
171 faulthandler.enable()
172 faulthandler._sigabrt()
173 """,
Victor Stinnerd727e232011-04-01 12:13:55 +0200174 3,
175 'Aborted')
176
Victor Stinner024e37a2011-03-31 01:31:06 +0200177 @unittest.skipIf(sys.platform == 'win32',
178 "SIGFPE cannot be caught on Windows")
179 def test_sigfpe(self):
180 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200181 import faulthandler
182 faulthandler.enable()
183 faulthandler._sigfpe()
184 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200185 3,
186 'Floating point exception')
187
Victor Stinner56e8c292014-07-21 12:30:22 +0200188 @unittest.skipIf(_testcapi is None, 'need _testcapi')
189 @unittest.skipUnless(hasattr(signal, 'SIGBUS'), 'need signal.SIGBUS')
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100190 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200191 def test_sigbus(self):
192 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200193 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200194 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200195 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200196
Victor Stinner6d201682014-08-10 19:50:08 +0200197 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200198 _testcapi.raise_signal(signal.SIGBUS)
Victor Stinner6d201682014-08-10 19:50:08 +0200199 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200200 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200201 'Bus error')
202
Victor Stinner56e8c292014-07-21 12:30:22 +0200203 @unittest.skipIf(_testcapi is None, 'need _testcapi')
204 @unittest.skipUnless(hasattr(signal, 'SIGILL'), 'need signal.SIGILL')
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100205 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200206 def test_sigill(self):
207 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200208 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200209 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200210 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200211
Victor Stinner6d201682014-08-10 19:50:08 +0200212 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200213 _testcapi.raise_signal(signal.SIGILL)
Victor Stinner6d201682014-08-10 19:50:08 +0200214 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200215 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200216 'Illegal instruction')
217
218 def test_fatal_error(self):
219 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200220 import faulthandler
221 faulthandler._fatal_error(b'xyz')
222 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200223 2,
224 'xyz')
225
Victor Stinner57003f82016-03-15 17:23:35 +0100226 def test_fatal_error_without_gil(self):
227 self.check_fatal_error("""
228 import faulthandler
229 faulthandler._fatal_error(b'xyz', True)
230 """,
231 2,
232 'xyz')
233
Charles-François Natali3391e642011-09-01 23:08:21 +0200234 @unittest.skipIf(sys.platform.startswith('openbsd') and HAVE_THREADS,
235 "Issue #12868: sigaltstack() doesn't work on "
236 "OpenBSD if Python is compiled with pthread")
Victor Stinner024e37a2011-03-31 01:31:06 +0200237 @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'),
238 'need faulthandler._stack_overflow()')
239 def test_stack_overflow(self):
240 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200241 import faulthandler
242 faulthandler.enable()
243 faulthandler._stack_overflow()
244 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200245 3,
Victor Stinnerf0480752011-03-31 11:34:08 +0200246 '(?:Segmentation fault|Bus error)',
247 other_regex='unable to raise a stack overflow')
Victor Stinner024e37a2011-03-31 01:31:06 +0200248
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100249 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200250 def test_gil_released(self):
251 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200252 import faulthandler
253 faulthandler.enable()
Victor Stinner50838282014-09-30 13:54:14 +0200254 faulthandler._sigsegv(True)
Victor Stinner6d201682014-08-10 19:50:08 +0200255 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200256 3,
Victor Stinner50838282014-09-30 13:54:14 +0200257 'Segmentation fault')
Victor Stinner024e37a2011-03-31 01:31:06 +0200258
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100259 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200260 def test_enable_file(self):
261 with temporary_filename() as filename:
262 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200263 import faulthandler
264 output = open({filename}, 'wb')
265 faulthandler.enable(output)
266 faulthandler._sigsegv()
267 """.format(filename=repr(filename)),
Victor Stinner024e37a2011-03-31 01:31:06 +0200268 4,
Victor Stinner56785392013-06-17 23:37:59 +0200269 'Segmentation fault',
Victor Stinner024e37a2011-03-31 01:31:06 +0200270 filename=filename)
271
Victor Stinnerff2a6612015-03-13 11:01:30 +0100272 @unittest.skipIf(sys.platform == "win32",
273 "subprocess doesn't support pass_fds on Windows")
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100274 @requires_raise
Victor Stinner95bb7142015-03-12 15:32:03 +0100275 def test_enable_fd(self):
276 with tempfile.TemporaryFile('wb+') as fp:
277 fd = fp.fileno()
278 self.check_fatal_error("""
279 import faulthandler
280 import sys
281 faulthandler.enable(%s)
282 faulthandler._sigsegv()
283 """ % fd,
284 4,
285 'Segmentation fault',
286 fd=fd)
287
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100288 @requires_raise
Victor Stinner7bba62f2011-05-07 12:43:00 +0200289 def test_enable_single_thread(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200290 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200291 import faulthandler
292 faulthandler.enable(all_threads=False)
293 faulthandler._sigsegv()
294 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200295 3,
Victor Stinner56785392013-06-17 23:37:59 +0200296 'Segmentation fault',
Victor Stinner7bba62f2011-05-07 12:43:00 +0200297 all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200298
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100299 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200300 def test_disable(self):
301 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200302 import faulthandler
303 faulthandler.enable()
304 faulthandler.disable()
305 faulthandler._sigsegv()
306 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200307 not_expected = 'Fatal Python error'
Antoine Pitrou77e904e2013-10-08 23:04:32 +0200308 stderr, exitcode = self.get_output(code)
Victor Stinner29001c82014-09-25 00:38:48 +0200309 stderr = '\n'.join(stderr)
Victor Stinner024e37a2011-03-31 01:31:06 +0200310 self.assertTrue(not_expected not in stderr,
311 "%r is present in %r" % (not_expected, stderr))
Victor Stinner05585cb2011-03-31 13:29:56 +0200312 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200313
314 def test_is_enabled(self):
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200315 orig_stderr = sys.stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200316 try:
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200317 # regrtest may replace sys.stderr by io.StringIO object, but
318 # faulthandler.enable() requires that sys.stderr has a fileno()
319 # method
Victor Stinner72488502011-06-29 23:24:31 +0200320 sys.stderr = sys.__stderr__
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200321
322 was_enabled = faulthandler.is_enabled()
323 try:
Victor Stinner024e37a2011-03-31 01:31:06 +0200324 faulthandler.enable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200325 self.assertTrue(faulthandler.is_enabled())
Victor Stinner024e37a2011-03-31 01:31:06 +0200326 faulthandler.disable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200327 self.assertFalse(faulthandler.is_enabled())
328 finally:
329 if was_enabled:
330 faulthandler.enable()
331 else:
332 faulthandler.disable()
333 finally:
334 sys.stderr = orig_stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200335
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200336 def test_disabled_by_default(self):
337 # By default, the module should be disabled
338 code = "import faulthandler; print(faulthandler.is_enabled())"
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800339 args = filter(None, (sys.executable,
340 "-E" if sys.flags.ignore_environment else "",
341 "-c", code))
342 env = os.environ.copy()
343 env.pop("PYTHONFAULTHANDLER", None)
344 # don't use assert_python_ok() because it always enables faulthandler
345 output = subprocess.check_output(args, env=env)
Victor Stinner88983502013-09-08 11:36:23 +0200346 self.assertEqual(output.rstrip(), b"False")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200347
348 def test_sys_xoptions(self):
349 # Test python -X faulthandler
350 code = "import faulthandler; print(faulthandler.is_enabled())"
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800351 args = filter(None, (sys.executable,
352 "-E" if sys.flags.ignore_environment else "",
353 "-X", "faulthandler", "-c", code))
354 env = os.environ.copy()
355 env.pop("PYTHONFAULTHANDLER", None)
356 # don't use assert_python_ok() because it always enables faulthandler
357 output = subprocess.check_output(args, env=env)
Victor Stinner88983502013-09-08 11:36:23 +0200358 self.assertEqual(output.rstrip(), b"True")
359
360 def test_env_var(self):
361 # empty env var
362 code = "import faulthandler; print(faulthandler.is_enabled())"
363 args = (sys.executable, "-c", code)
364 env = os.environ.copy()
365 env['PYTHONFAULTHANDLER'] = ''
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800366 # don't use assert_python_ok() because it always enables faulthandler
Victor Stinner88983502013-09-08 11:36:23 +0200367 output = subprocess.check_output(args, env=env)
368 self.assertEqual(output.rstrip(), b"False")
369
370 # non-empty env var
371 env = os.environ.copy()
372 env['PYTHONFAULTHANDLER'] = '1'
373 output = subprocess.check_output(args, env=env)
374 self.assertEqual(output.rstrip(), b"True")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200375
Victor Stinner95bb7142015-03-12 15:32:03 +0100376 def check_dump_traceback(self, *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200377 """
378 Explicitly call dump_traceback() function and check its output.
379 Raise an error if the output doesn't match the expected format.
380 """
381 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200382 import faulthandler
Victor Stinner024e37a2011-03-31 01:31:06 +0200383
Victor Stinner95bb7142015-03-12 15:32:03 +0100384 filename = {filename!r}
385 fd = {fd}
386
Victor Stinner6d201682014-08-10 19:50:08 +0200387 def funcB():
Victor Stinner95bb7142015-03-12 15:32:03 +0100388 if filename:
389 with open(filename, "wb") as fp:
Victor Stinner6d201682014-08-10 19:50:08 +0200390 faulthandler.dump_traceback(fp, all_threads=False)
Victor Stinner95bb7142015-03-12 15:32:03 +0100391 elif fd is not None:
392 faulthandler.dump_traceback(fd,
393 all_threads=False)
Victor Stinner6d201682014-08-10 19:50:08 +0200394 else:
395 faulthandler.dump_traceback(all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200396
Victor Stinner6d201682014-08-10 19:50:08 +0200397 def funcA():
398 funcB()
Victor Stinner024e37a2011-03-31 01:31:06 +0200399
Victor Stinner6d201682014-08-10 19:50:08 +0200400 funcA()
401 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200402 code = code.format(
Victor Stinner95bb7142015-03-12 15:32:03 +0100403 filename=filename,
404 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200405 )
406 if filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100407 lineno = 9
408 elif fd is not None:
409 lineno = 12
Victor Stinner024e37a2011-03-31 01:31:06 +0200410 else:
Victor Stinner95bb7142015-03-12 15:32:03 +0100411 lineno = 14
Victor Stinner024e37a2011-03-31 01:31:06 +0200412 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700413 'Stack (most recent call first):',
Victor Stinner024e37a2011-03-31 01:31:06 +0200414 ' File "<string>", line %s in funcB' % lineno,
Victor Stinner95bb7142015-03-12 15:32:03 +0100415 ' File "<string>", line 17 in funcA',
416 ' File "<string>", line 19 in <module>'
Victor Stinner024e37a2011-03-31 01:31:06 +0200417 ]
Victor Stinner95bb7142015-03-12 15:32:03 +0100418 trace, exitcode = self.get_output(code, filename, fd)
Victor Stinner024e37a2011-03-31 01:31:06 +0200419 self.assertEqual(trace, expected)
Victor Stinner05585cb2011-03-31 13:29:56 +0200420 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200421
422 def test_dump_traceback(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100423 self.check_dump_traceback()
Victor Stinner19402332011-03-31 18:15:52 +0200424
425 def test_dump_traceback_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200426 with temporary_filename() as filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100427 self.check_dump_traceback(filename=filename)
428
Victor Stinnerff2a6612015-03-13 11:01:30 +0100429 @unittest.skipIf(sys.platform == "win32",
430 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100431 def test_dump_traceback_fd(self):
432 with tempfile.TemporaryFile('wb+') as fp:
433 self.check_dump_traceback(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200434
Victor Stinner53386d82012-08-01 19:45:34 +0200435 def test_truncate(self):
436 maxlen = 500
437 func_name = 'x' * (maxlen + 50)
438 truncated = 'x' * maxlen + '...'
439 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200440 import faulthandler
Victor Stinner53386d82012-08-01 19:45:34 +0200441
Victor Stinner6d201682014-08-10 19:50:08 +0200442 def {func_name}():
443 faulthandler.dump_traceback(all_threads=False)
Victor Stinner53386d82012-08-01 19:45:34 +0200444
Victor Stinner6d201682014-08-10 19:50:08 +0200445 {func_name}()
446 """
Victor Stinner53386d82012-08-01 19:45:34 +0200447 code = code.format(
448 func_name=func_name,
449 )
450 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700451 'Stack (most recent call first):',
Victor Stinner53386d82012-08-01 19:45:34 +0200452 ' File "<string>", line 4 in %s' % truncated,
453 ' File "<string>", line 6 in <module>'
454 ]
455 trace, exitcode = self.get_output(code)
456 self.assertEqual(trace, expected)
457 self.assertEqual(exitcode, 0)
458
Victor Stinnerff4cd882011-04-07 11:50:25 +0200459 @unittest.skipIf(not HAVE_THREADS, 'need threads')
Victor Stinner024e37a2011-03-31 01:31:06 +0200460 def check_dump_traceback_threads(self, filename):
461 """
462 Call explicitly dump_traceback(all_threads=True) and check the output.
463 Raise an error if the output doesn't match the expected format.
464 """
465 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200466 import faulthandler
467 from threading import Thread, Event
468 import time
Victor Stinner024e37a2011-03-31 01:31:06 +0200469
Victor Stinner6d201682014-08-10 19:50:08 +0200470 def dump():
471 if {filename}:
472 with open({filename}, "wb") as fp:
473 faulthandler.dump_traceback(fp, all_threads=True)
474 else:
475 faulthandler.dump_traceback(all_threads=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200476
Victor Stinner6d201682014-08-10 19:50:08 +0200477 class Waiter(Thread):
478 # avoid blocking if the main thread raises an exception.
479 daemon = True
Victor Stinner024e37a2011-03-31 01:31:06 +0200480
Victor Stinner6d201682014-08-10 19:50:08 +0200481 def __init__(self):
482 Thread.__init__(self)
483 self.running = Event()
484 self.stop = Event()
Victor Stinner024e37a2011-03-31 01:31:06 +0200485
Victor Stinner6d201682014-08-10 19:50:08 +0200486 def run(self):
487 self.running.set()
488 self.stop.wait()
Victor Stinner024e37a2011-03-31 01:31:06 +0200489
Victor Stinner6d201682014-08-10 19:50:08 +0200490 waiter = Waiter()
491 waiter.start()
492 waiter.running.wait()
493 dump()
494 waiter.stop.set()
495 waiter.join()
496 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200497 code = code.format(filename=repr(filename))
Victor Stinner05585cb2011-03-31 13:29:56 +0200498 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200499 output = '\n'.join(output)
500 if filename:
501 lineno = 8
502 else:
503 lineno = 10
R David Murray44b548d2016-09-08 13:59:53 -0400504 regex = r"""
Victor Stinner6d201682014-08-10 19:50:08 +0200505 ^Thread 0x[0-9a-f]+ \(most recent call first\):
506 (?: File ".*threading.py", line [0-9]+ in [_a-z]+
507 ){{1,3}} File "<string>", line 23 in run
508 File ".*threading.py", line [0-9]+ in _bootstrap_inner
509 File ".*threading.py", line [0-9]+ in _bootstrap
Victor Stinner024e37a2011-03-31 01:31:06 +0200510
Victor Stinner861d9ab2016-03-16 22:45:24 +0100511 Current thread 0x[0-9a-f]+ \(most recent call first\):
Victor Stinner6d201682014-08-10 19:50:08 +0200512 File "<string>", line {lineno} in dump
513 File "<string>", line 28 in <module>$
514 """
515 regex = dedent(regex.format(lineno=lineno)).strip()
Victor Stinner024e37a2011-03-31 01:31:06 +0200516 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200517 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200518
519 def test_dump_traceback_threads(self):
520 self.check_dump_traceback_threads(None)
Victor Stinner19402332011-03-31 18:15:52 +0200521
522 def test_dump_traceback_threads_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200523 with temporary_filename() as filename:
524 self.check_dump_traceback_threads(filename)
525
Victor Stinner95bb7142015-03-12 15:32:03 +0100526 @unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'),
527 'need faulthandler.dump_traceback_later()')
528 def check_dump_traceback_later(self, repeat=False, cancel=False, loops=1,
529 *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200530 """
531 Check how many times the traceback is written in timeout x 2.5 seconds,
532 or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending
533 on repeat and cancel options.
534
535 Raise an error if the output doesn't match the expect format.
536 """
Victor Stinnerc790a532011-04-08 13:39:59 +0200537 timeout_str = str(datetime.timedelta(seconds=TIMEOUT))
Victor Stinner024e37a2011-03-31 01:31:06 +0200538 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200539 import faulthandler
540 import time
Victor Stinner95bb7142015-03-12 15:32:03 +0100541 import sys
542
543 timeout = {timeout}
544 repeat = {repeat}
545 cancel = {cancel}
546 loops = {loops}
547 filename = {filename!r}
548 fd = {fd}
Victor Stinner024e37a2011-03-31 01:31:06 +0200549
Victor Stinner6d201682014-08-10 19:50:08 +0200550 def func(timeout, repeat, cancel, file, loops):
551 for loop in range(loops):
552 faulthandler.dump_traceback_later(timeout, repeat=repeat, file=file)
553 if cancel:
554 faulthandler.cancel_dump_traceback_later()
555 time.sleep(timeout * 5)
556 faulthandler.cancel_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200557
Victor Stinner95bb7142015-03-12 15:32:03 +0100558 if filename:
559 file = open(filename, "wb")
560 elif fd is not None:
561 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200562 else:
563 file = None
564 func(timeout, repeat, cancel, file, loops)
Victor Stinner95bb7142015-03-12 15:32:03 +0100565 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200566 file.close()
567 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200568 code = code.format(
Victor Stinnerde10f402011-04-08 12:57:06 +0200569 timeout=TIMEOUT,
Victor Stinner024e37a2011-03-31 01:31:06 +0200570 repeat=repeat,
571 cancel=cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200572 loops=loops,
Victor Stinner95bb7142015-03-12 15:32:03 +0100573 filename=filename,
574 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200575 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200576 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200577 trace = '\n'.join(trace)
578
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200579 if not cancel:
Victor Stinnerde10f402011-04-08 12:57:06 +0200580 count = loops
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200581 if repeat:
Victor Stinnerde10f402011-04-08 12:57:06 +0200582 count *= 2
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700583 header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+ \(most recent call first\):\n' % timeout_str
Victor Stinner95bb7142015-03-12 15:32:03 +0100584 regex = expected_traceback(17, 26, header, min_count=count)
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200585 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200586 else:
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200587 self.assertEqual(trace, '')
Victor Stinner05585cb2011-03-31 13:29:56 +0200588 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200589
Georg Brandldeb92b52012-09-22 08:58:55 +0200590 def test_dump_traceback_later(self):
591 self.check_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200592
Georg Brandldeb92b52012-09-22 08:58:55 +0200593 def test_dump_traceback_later_repeat(self):
594 self.check_dump_traceback_later(repeat=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200595
Georg Brandldeb92b52012-09-22 08:58:55 +0200596 def test_dump_traceback_later_cancel(self):
597 self.check_dump_traceback_later(cancel=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200598
Georg Brandldeb92b52012-09-22 08:58:55 +0200599 def test_dump_traceback_later_file(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100600 with temporary_filename() as filename:
601 self.check_dump_traceback_later(filename=filename)
602
Victor Stinnerff2a6612015-03-13 11:01:30 +0100603 @unittest.skipIf(sys.platform == "win32",
604 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100605 def test_dump_traceback_later_fd(self):
606 with tempfile.TemporaryFile('wb+') as fp:
607 self.check_dump_traceback_later(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200608
Georg Brandldeb92b52012-09-22 08:58:55 +0200609 def test_dump_traceback_later_twice(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100610 self.check_dump_traceback_later(loops=2)
Victor Stinnerde10f402011-04-08 12:57:06 +0200611
Victor Stinner024e37a2011-03-31 01:31:06 +0200612 @unittest.skipIf(not hasattr(faulthandler, "register"),
613 "need faulthandler.register")
Victor Stinnera01ca122011-04-01 12:56:17 +0200614 def check_register(self, filename=False, all_threads=False,
Victor Stinner95bb7142015-03-12 15:32:03 +0100615 unregister=False, chain=False, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200616 """
617 Register a handler displaying the traceback on a user signal. Raise the
618 signal and check the written traceback.
619
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200620 If chain is True, check that the previous signal handler is called.
621
Victor Stinner024e37a2011-03-31 01:31:06 +0200622 Raise an error if the output doesn't match the expected format.
623 """
Victor Stinnera01ca122011-04-01 12:56:17 +0200624 signum = signal.SIGUSR1
Victor Stinner024e37a2011-03-31 01:31:06 +0200625 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200626 import faulthandler
627 import os
628 import signal
629 import sys
Victor Stinner024e37a2011-03-31 01:31:06 +0200630
Victor Stinner95bb7142015-03-12 15:32:03 +0100631 all_threads = {all_threads}
632 signum = {signum}
633 unregister = {unregister}
634 chain = {chain}
635 filename = {filename!r}
636 fd = {fd}
637
Victor Stinner6d201682014-08-10 19:50:08 +0200638 def func(signum):
639 os.kill(os.getpid(), signum)
Victor Stinner024e37a2011-03-31 01:31:06 +0200640
Victor Stinner6d201682014-08-10 19:50:08 +0200641 def handler(signum, frame):
642 handler.called = True
643 handler.called = False
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200644
Victor Stinner95bb7142015-03-12 15:32:03 +0100645 if filename:
646 file = open(filename, "wb")
647 elif fd is not None:
648 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200649 else:
650 file = None
651 if chain:
652 signal.signal(signum, handler)
653 faulthandler.register(signum, file=file,
Victor Stinner95bb7142015-03-12 15:32:03 +0100654 all_threads=all_threads, chain={chain})
Victor Stinner6d201682014-08-10 19:50:08 +0200655 if unregister:
656 faulthandler.unregister(signum)
657 func(signum)
658 if chain and not handler.called:
659 if file is not None:
660 output = file
661 else:
662 output = sys.stderr
663 print("Error: signal handler not called!", file=output)
664 exitcode = 1
Victor Stinner95bb7142015-03-12 15:32:03 +0100665 else:
666 exitcode = 0
667 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200668 file.close()
669 sys.exit(exitcode)
670 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200671 code = code.format(
Victor Stinner024e37a2011-03-31 01:31:06 +0200672 all_threads=all_threads,
Victor Stinnera01ca122011-04-01 12:56:17 +0200673 signum=signum,
674 unregister=unregister,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200675 chain=chain,
Victor Stinner95bb7142015-03-12 15:32:03 +0100676 filename=filename,
677 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200678 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200679 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200680 trace = '\n'.join(trace)
Victor Stinnera01ca122011-04-01 12:56:17 +0200681 if not unregister:
682 if all_threads:
R David Murray44b548d2016-09-08 13:59:53 -0400683 regex = r'Current thread 0x[0-9a-f]+ \(most recent call first\):\n'
Victor Stinnera01ca122011-04-01 12:56:17 +0200684 else:
R David Murray44b548d2016-09-08 13:59:53 -0400685 regex = r'Stack \(most recent call first\):\n'
Victor Stinner95bb7142015-03-12 15:32:03 +0100686 regex = expected_traceback(14, 32, regex)
Victor Stinnera01ca122011-04-01 12:56:17 +0200687 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200688 else:
Victor Stinnera01ca122011-04-01 12:56:17 +0200689 self.assertEqual(trace, '')
690 if unregister:
691 self.assertNotEqual(exitcode, 0)
692 else:
693 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200694
695 def test_register(self):
696 self.check_register()
697
Victor Stinnera01ca122011-04-01 12:56:17 +0200698 def test_unregister(self):
699 self.check_register(unregister=True)
700
Victor Stinner024e37a2011-03-31 01:31:06 +0200701 def test_register_file(self):
702 with temporary_filename() as filename:
703 self.check_register(filename=filename)
704
Victor Stinnerff2a6612015-03-13 11:01:30 +0100705 @unittest.skipIf(sys.platform == "win32",
706 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100707 def test_register_fd(self):
708 with tempfile.TemporaryFile('wb+') as fp:
709 self.check_register(fd=fp.fileno())
710
Victor Stinner024e37a2011-03-31 01:31:06 +0200711 def test_register_threads(self):
712 self.check_register(all_threads=True)
713
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200714 def test_register_chain(self):
715 self.check_register(chain=True)
716
Victor Stinnere2d66902014-05-14 17:15:50 +0200717 @contextmanager
718 def check_stderr_none(self):
719 stderr = sys.stderr
720 try:
721 sys.stderr = None
722 with self.assertRaises(RuntimeError) as cm:
723 yield
724 self.assertEqual(str(cm.exception), "sys.stderr is None")
725 finally:
726 sys.stderr = stderr
727
728 def test_stderr_None(self):
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +0300729 # Issue #21497: provide a helpful error if sys.stderr is None,
Victor Stinnere2d66902014-05-14 17:15:50 +0200730 # instead of just an attribute error: "None has no attribute fileno".
731 with self.check_stderr_none():
732 faulthandler.enable()
733 with self.check_stderr_none():
734 faulthandler.dump_traceback()
735 if hasattr(faulthandler, 'dump_traceback_later'):
736 with self.check_stderr_none():
737 faulthandler.dump_traceback_later(1e-3)
738 if hasattr(faulthandler, "register"):
739 with self.check_stderr_none():
740 faulthandler.register(signal.SIGUSR1)
741
Victor Stinner404cdc52016-03-23 10:39:17 +0100742 @unittest.skipUnless(MS_WINDOWS, 'specific to Windows')
743 def test_raise_exception(self):
744 for exc, name in (
745 ('EXCEPTION_ACCESS_VIOLATION', 'access violation'),
746 ('EXCEPTION_INT_DIVIDE_BY_ZERO', 'int divide by zero'),
747 ('EXCEPTION_STACK_OVERFLOW', 'stack overflow'),
748 ):
Eric V. Smith451d0e32016-09-09 21:56:20 -0400749 self.check_windows_exception(f"""
Victor Stinner404cdc52016-03-23 10:39:17 +0100750 import faulthandler
751 faulthandler.enable()
752 faulthandler._raise_exception(faulthandler._{exc})
Eric V. Smith451d0e32016-09-09 21:56:20 -0400753 """,
Victor Stinner404cdc52016-03-23 10:39:17 +0100754 3,
755 name)
756
757
Victor Stinner024e37a2011-03-31 01:31:06 +0200758
Victor Stinner024e37a2011-03-31 01:31:06 +0200759if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400760 unittest.main()