blob: 1562eecbd65811b307ae74ba8df4b507b8da86ff [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 re
6import signal
7import subprocess
8import sys
Berker Peksagce643912015-05-06 06:33:17 +03009from test import support
10from test.support import script_helper
Victor Stinner024e37a2011-03-31 01:31:06 +020011import tempfile
12import 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:
16 import threading
17 HAVE_THREADS = True
18except ImportError:
19 HAVE_THREADS = False
Victor Stinner56e8c292014-07-21 12:30:22 +020020try:
21 import _testcapi
22except ImportError:
23 _testcapi = None
Victor Stinnerff4cd882011-04-07 11:50:25 +020024
Victor Stinner44378d42011-04-01 15:37:12 +020025TIMEOUT = 0.5
26
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
44class 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 Stinner024e37a2011-03-31 01:31:06 +020061 stdout, stderr = process.communicate()
62 exitcode = process.wait()
Victor Stinner19402332011-03-31 18:15:52 +020063 output = support.strip_python_stderr(stdout)
64 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020065 if filename:
Victor Stinner19402332011-03-31 18:15:52 +020066 self.assertEqual(output, '')
Victor Stinner024e37a2011-03-31 01:31:06 +020067 with open(filename, "rb") as fp:
68 output = fp.read()
Victor Stinner19402332011-03-31 18:15:52 +020069 output = output.decode('ascii', 'backslashreplace')
Victor Stinner95bb7142015-03-12 15:32:03 +010070 elif fd is not None:
71 self.assertEqual(output, '')
72 os.lseek(fd, os.SEEK_SET, 0)
73 with open(fd, "rb", closefd=False) as fp:
74 output = fp.read()
75 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020076 output = re.sub('Current thread 0x[0-9a-f]+',
77 'Current thread XXX',
78 output)
Victor Stinner05585cb2011-03-31 13:29:56 +020079 return output.splitlines(), exitcode
Victor Stinner024e37a2011-03-31 01:31:06 +020080
81 def check_fatal_error(self, code, line_number, name_regex,
Victor Stinner95bb7142015-03-12 15:32:03 +010082 filename=None, all_threads=True, other_regex=None,
83 fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020084 """
85 Check that the fault handler for fatal errors is enabled and check the
86 traceback from the child process output.
87
88 Raise an error if the output doesn't match the expected format.
89 """
90 if all_threads:
Guido van Rossum7be5d7d2013-10-20 18:21:02 -070091 header = 'Current thread XXX (most recent call first)'
Victor Stinner024e37a2011-03-31 01:31:06 +020092 else:
Guido van Rossum7be5d7d2013-10-20 18:21:02 -070093 header = 'Stack (most recent call first)'
Victor Stinner024e37a2011-03-31 01:31:06 +020094 regex = """
Victor Stinner6d201682014-08-10 19:50:08 +020095 ^Fatal Python error: {name}
Victor Stinner024e37a2011-03-31 01:31:06 +020096
Victor Stinner6d201682014-08-10 19:50:08 +020097 {header}:
98 File "<string>", line {lineno} in <module>
99 """
100 regex = dedent(regex.format(
Victor Stinner024e37a2011-03-31 01:31:06 +0200101 lineno=line_number,
102 name=name_regex,
Victor Stinner6d201682014-08-10 19:50:08 +0200103 header=re.escape(header))).strip()
Victor Stinnerf0480752011-03-31 11:34:08 +0200104 if other_regex:
105 regex += '|' + other_regex
Victor Stinner95bb7142015-03-12 15:32:03 +0100106 output, exitcode = self.get_output(code, filename=filename, fd=fd)
Victor Stinner024e37a2011-03-31 01:31:06 +0200107 output = '\n'.join(output)
108 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200109 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200110
Victor Stinner330426c2013-07-03 22:29:42 +0200111 @unittest.skipIf(sys.platform.startswith('aix'),
112 "the first page of memory is a mapped read-only on AIX")
Victor Stinner024e37a2011-03-31 01:31:06 +0200113 def test_read_null(self):
114 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200115 import faulthandler
116 faulthandler.enable()
117 faulthandler._read_null()
118 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200119 3,
Victor Stinnera8db3782011-08-08 22:43:45 +0200120 # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion
121 '(?:Segmentation fault|Bus error|Illegal instruction)')
Victor Stinner024e37a2011-03-31 01:31:06 +0200122
123 def test_sigsegv(self):
124 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200125 import faulthandler
126 faulthandler.enable()
127 faulthandler._sigsegv()
128 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200129 3,
130 'Segmentation fault')
131
Victor Stinnerd727e232011-04-01 12:13:55 +0200132 def test_sigabrt(self):
133 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200134 import faulthandler
135 faulthandler.enable()
136 faulthandler._sigabrt()
137 """,
Victor Stinnerd727e232011-04-01 12:13:55 +0200138 3,
139 'Aborted')
140
Victor Stinner024e37a2011-03-31 01:31:06 +0200141 @unittest.skipIf(sys.platform == 'win32',
142 "SIGFPE cannot be caught on Windows")
143 def test_sigfpe(self):
144 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200145 import faulthandler
146 faulthandler.enable()
147 faulthandler._sigfpe()
148 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200149 3,
150 'Floating point exception')
151
Victor Stinner56e8c292014-07-21 12:30:22 +0200152 @unittest.skipIf(_testcapi is None, 'need _testcapi')
153 @unittest.skipUnless(hasattr(signal, 'SIGBUS'), 'need signal.SIGBUS')
Victor Stinner024e37a2011-03-31 01:31:06 +0200154 def test_sigbus(self):
155 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200156 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200157 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200158 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200159
Victor Stinner6d201682014-08-10 19:50:08 +0200160 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200161 _testcapi.raise_signal(signal.SIGBUS)
Victor Stinner6d201682014-08-10 19:50:08 +0200162 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200163 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200164 'Bus error')
165
Victor Stinner56e8c292014-07-21 12:30:22 +0200166 @unittest.skipIf(_testcapi is None, 'need _testcapi')
167 @unittest.skipUnless(hasattr(signal, 'SIGILL'), 'need signal.SIGILL')
Victor Stinner024e37a2011-03-31 01:31:06 +0200168 def test_sigill(self):
169 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200170 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200171 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200172 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200173
Victor Stinner6d201682014-08-10 19:50:08 +0200174 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200175 _testcapi.raise_signal(signal.SIGILL)
Victor Stinner6d201682014-08-10 19:50:08 +0200176 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200177 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200178 'Illegal instruction')
179
180 def test_fatal_error(self):
181 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200182 import faulthandler
183 faulthandler._fatal_error(b'xyz')
184 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200185 2,
186 'xyz')
187
Victor Stinner57003f82016-03-15 17:23:35 +0100188 def test_fatal_error_without_gil(self):
189 self.check_fatal_error("""
190 import faulthandler
191 faulthandler._fatal_error(b'xyz', True)
192 """,
193 2,
194 'xyz')
195
Charles-François Natali3391e642011-09-01 23:08:21 +0200196 @unittest.skipIf(sys.platform.startswith('openbsd') and HAVE_THREADS,
197 "Issue #12868: sigaltstack() doesn't work on "
198 "OpenBSD if Python is compiled with pthread")
Victor Stinner024e37a2011-03-31 01:31:06 +0200199 @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'),
200 'need faulthandler._stack_overflow()')
201 def test_stack_overflow(self):
202 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200203 import faulthandler
204 faulthandler.enable()
205 faulthandler._stack_overflow()
206 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200207 3,
Victor Stinnerf0480752011-03-31 11:34:08 +0200208 '(?:Segmentation fault|Bus error)',
209 other_regex='unable to raise a stack overflow')
Victor Stinner024e37a2011-03-31 01:31:06 +0200210
211 def test_gil_released(self):
212 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200213 import faulthandler
214 faulthandler.enable()
Victor Stinner50838282014-09-30 13:54:14 +0200215 faulthandler._sigsegv(True)
Victor Stinner6d201682014-08-10 19:50:08 +0200216 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200217 3,
Victor Stinner50838282014-09-30 13:54:14 +0200218 'Segmentation fault')
Victor Stinner024e37a2011-03-31 01:31:06 +0200219
220 def test_enable_file(self):
221 with temporary_filename() as filename:
222 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200223 import faulthandler
224 output = open({filename}, 'wb')
225 faulthandler.enable(output)
226 faulthandler._sigsegv()
227 """.format(filename=repr(filename)),
Victor Stinner024e37a2011-03-31 01:31:06 +0200228 4,
Victor Stinner56785392013-06-17 23:37:59 +0200229 'Segmentation fault',
Victor Stinner024e37a2011-03-31 01:31:06 +0200230 filename=filename)
231
Victor Stinnerff2a6612015-03-13 11:01:30 +0100232 @unittest.skipIf(sys.platform == "win32",
233 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100234 def test_enable_fd(self):
235 with tempfile.TemporaryFile('wb+') as fp:
236 fd = fp.fileno()
237 self.check_fatal_error("""
238 import faulthandler
239 import sys
240 faulthandler.enable(%s)
241 faulthandler._sigsegv()
242 """ % fd,
243 4,
244 'Segmentation fault',
245 fd=fd)
246
Victor Stinner7bba62f2011-05-07 12:43:00 +0200247 def test_enable_single_thread(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200248 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200249 import faulthandler
250 faulthandler.enable(all_threads=False)
251 faulthandler._sigsegv()
252 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200253 3,
Victor Stinner56785392013-06-17 23:37:59 +0200254 'Segmentation fault',
Victor Stinner7bba62f2011-05-07 12:43:00 +0200255 all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200256
257 def test_disable(self):
258 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200259 import faulthandler
260 faulthandler.enable()
261 faulthandler.disable()
262 faulthandler._sigsegv()
263 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200264 not_expected = 'Fatal Python error'
Antoine Pitrou77e904e2013-10-08 23:04:32 +0200265 stderr, exitcode = self.get_output(code)
Victor Stinner29001c82014-09-25 00:38:48 +0200266 stderr = '\n'.join(stderr)
Victor Stinner024e37a2011-03-31 01:31:06 +0200267 self.assertTrue(not_expected not in stderr,
268 "%r is present in %r" % (not_expected, stderr))
Victor Stinner05585cb2011-03-31 13:29:56 +0200269 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200270
271 def test_is_enabled(self):
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200272 orig_stderr = sys.stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200273 try:
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200274 # regrtest may replace sys.stderr by io.StringIO object, but
275 # faulthandler.enable() requires that sys.stderr has a fileno()
276 # method
Victor Stinner72488502011-06-29 23:24:31 +0200277 sys.stderr = sys.__stderr__
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200278
279 was_enabled = faulthandler.is_enabled()
280 try:
Victor Stinner024e37a2011-03-31 01:31:06 +0200281 faulthandler.enable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200282 self.assertTrue(faulthandler.is_enabled())
Victor Stinner024e37a2011-03-31 01:31:06 +0200283 faulthandler.disable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200284 self.assertFalse(faulthandler.is_enabled())
285 finally:
286 if was_enabled:
287 faulthandler.enable()
288 else:
289 faulthandler.disable()
290 finally:
291 sys.stderr = orig_stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200292
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200293 def test_disabled_by_default(self):
294 # By default, the module should be disabled
295 code = "import faulthandler; print(faulthandler.is_enabled())"
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800296 args = filter(None, (sys.executable,
297 "-E" if sys.flags.ignore_environment else "",
298 "-c", code))
299 env = os.environ.copy()
300 env.pop("PYTHONFAULTHANDLER", None)
301 # don't use assert_python_ok() because it always enables faulthandler
302 output = subprocess.check_output(args, env=env)
Victor Stinner88983502013-09-08 11:36:23 +0200303 self.assertEqual(output.rstrip(), b"False")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200304
305 def test_sys_xoptions(self):
306 # Test python -X faulthandler
307 code = "import faulthandler; print(faulthandler.is_enabled())"
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800308 args = filter(None, (sys.executable,
309 "-E" if sys.flags.ignore_environment else "",
310 "-X", "faulthandler", "-c", code))
311 env = os.environ.copy()
312 env.pop("PYTHONFAULTHANDLER", None)
313 # don't use assert_python_ok() because it always enables faulthandler
314 output = subprocess.check_output(args, env=env)
Victor Stinner88983502013-09-08 11:36:23 +0200315 self.assertEqual(output.rstrip(), b"True")
316
317 def test_env_var(self):
318 # empty env var
319 code = "import faulthandler; print(faulthandler.is_enabled())"
320 args = (sys.executable, "-c", code)
321 env = os.environ.copy()
322 env['PYTHONFAULTHANDLER'] = ''
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800323 # don't use assert_python_ok() because it always enables faulthandler
Victor Stinner88983502013-09-08 11:36:23 +0200324 output = subprocess.check_output(args, env=env)
325 self.assertEqual(output.rstrip(), b"False")
326
327 # non-empty env var
328 env = os.environ.copy()
329 env['PYTHONFAULTHANDLER'] = '1'
330 output = subprocess.check_output(args, env=env)
331 self.assertEqual(output.rstrip(), b"True")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200332
Victor Stinner95bb7142015-03-12 15:32:03 +0100333 def check_dump_traceback(self, *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200334 """
335 Explicitly call dump_traceback() function and check its output.
336 Raise an error if the output doesn't match the expected format.
337 """
338 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200339 import faulthandler
Victor Stinner024e37a2011-03-31 01:31:06 +0200340
Victor Stinner95bb7142015-03-12 15:32:03 +0100341 filename = {filename!r}
342 fd = {fd}
343
Victor Stinner6d201682014-08-10 19:50:08 +0200344 def funcB():
Victor Stinner95bb7142015-03-12 15:32:03 +0100345 if filename:
346 with open(filename, "wb") as fp:
Victor Stinner6d201682014-08-10 19:50:08 +0200347 faulthandler.dump_traceback(fp, all_threads=False)
Victor Stinner95bb7142015-03-12 15:32:03 +0100348 elif fd is not None:
349 faulthandler.dump_traceback(fd,
350 all_threads=False)
Victor Stinner6d201682014-08-10 19:50:08 +0200351 else:
352 faulthandler.dump_traceback(all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200353
Victor Stinner6d201682014-08-10 19:50:08 +0200354 def funcA():
355 funcB()
Victor Stinner024e37a2011-03-31 01:31:06 +0200356
Victor Stinner6d201682014-08-10 19:50:08 +0200357 funcA()
358 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200359 code = code.format(
Victor Stinner95bb7142015-03-12 15:32:03 +0100360 filename=filename,
361 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200362 )
363 if filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100364 lineno = 9
365 elif fd is not None:
366 lineno = 12
Victor Stinner024e37a2011-03-31 01:31:06 +0200367 else:
Victor Stinner95bb7142015-03-12 15:32:03 +0100368 lineno = 14
Victor Stinner024e37a2011-03-31 01:31:06 +0200369 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700370 'Stack (most recent call first):',
Victor Stinner024e37a2011-03-31 01:31:06 +0200371 ' File "<string>", line %s in funcB' % lineno,
Victor Stinner95bb7142015-03-12 15:32:03 +0100372 ' File "<string>", line 17 in funcA',
373 ' File "<string>", line 19 in <module>'
Victor Stinner024e37a2011-03-31 01:31:06 +0200374 ]
Victor Stinner95bb7142015-03-12 15:32:03 +0100375 trace, exitcode = self.get_output(code, filename, fd)
Victor Stinner024e37a2011-03-31 01:31:06 +0200376 self.assertEqual(trace, expected)
Victor Stinner05585cb2011-03-31 13:29:56 +0200377 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200378
379 def test_dump_traceback(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100380 self.check_dump_traceback()
Victor Stinner19402332011-03-31 18:15:52 +0200381
382 def test_dump_traceback_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200383 with temporary_filename() as filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100384 self.check_dump_traceback(filename=filename)
385
Victor Stinnerff2a6612015-03-13 11:01:30 +0100386 @unittest.skipIf(sys.platform == "win32",
387 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100388 def test_dump_traceback_fd(self):
389 with tempfile.TemporaryFile('wb+') as fp:
390 self.check_dump_traceback(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200391
Victor Stinner53386d82012-08-01 19:45:34 +0200392 def test_truncate(self):
393 maxlen = 500
394 func_name = 'x' * (maxlen + 50)
395 truncated = 'x' * maxlen + '...'
396 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200397 import faulthandler
Victor Stinner53386d82012-08-01 19:45:34 +0200398
Victor Stinner6d201682014-08-10 19:50:08 +0200399 def {func_name}():
400 faulthandler.dump_traceback(all_threads=False)
Victor Stinner53386d82012-08-01 19:45:34 +0200401
Victor Stinner6d201682014-08-10 19:50:08 +0200402 {func_name}()
403 """
Victor Stinner53386d82012-08-01 19:45:34 +0200404 code = code.format(
405 func_name=func_name,
406 )
407 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700408 'Stack (most recent call first):',
Victor Stinner53386d82012-08-01 19:45:34 +0200409 ' File "<string>", line 4 in %s' % truncated,
410 ' File "<string>", line 6 in <module>'
411 ]
412 trace, exitcode = self.get_output(code)
413 self.assertEqual(trace, expected)
414 self.assertEqual(exitcode, 0)
415
Victor Stinnerff4cd882011-04-07 11:50:25 +0200416 @unittest.skipIf(not HAVE_THREADS, 'need threads')
Victor Stinner024e37a2011-03-31 01:31:06 +0200417 def check_dump_traceback_threads(self, filename):
418 """
419 Call explicitly dump_traceback(all_threads=True) and check the output.
420 Raise an error if the output doesn't match the expected format.
421 """
422 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200423 import faulthandler
424 from threading import Thread, Event
425 import time
Victor Stinner024e37a2011-03-31 01:31:06 +0200426
Victor Stinner6d201682014-08-10 19:50:08 +0200427 def dump():
428 if {filename}:
429 with open({filename}, "wb") as fp:
430 faulthandler.dump_traceback(fp, all_threads=True)
431 else:
432 faulthandler.dump_traceback(all_threads=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200433
Victor Stinner6d201682014-08-10 19:50:08 +0200434 class Waiter(Thread):
435 # avoid blocking if the main thread raises an exception.
436 daemon = True
Victor Stinner024e37a2011-03-31 01:31:06 +0200437
Victor Stinner6d201682014-08-10 19:50:08 +0200438 def __init__(self):
439 Thread.__init__(self)
440 self.running = Event()
441 self.stop = Event()
Victor Stinner024e37a2011-03-31 01:31:06 +0200442
Victor Stinner6d201682014-08-10 19:50:08 +0200443 def run(self):
444 self.running.set()
445 self.stop.wait()
Victor Stinner024e37a2011-03-31 01:31:06 +0200446
Victor Stinner6d201682014-08-10 19:50:08 +0200447 waiter = Waiter()
448 waiter.start()
449 waiter.running.wait()
450 dump()
451 waiter.stop.set()
452 waiter.join()
453 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200454 code = code.format(filename=repr(filename))
Victor Stinner05585cb2011-03-31 13:29:56 +0200455 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200456 output = '\n'.join(output)
457 if filename:
458 lineno = 8
459 else:
460 lineno = 10
461 regex = """
Victor Stinner6d201682014-08-10 19:50:08 +0200462 ^Thread 0x[0-9a-f]+ \(most recent call first\):
463 (?: File ".*threading.py", line [0-9]+ in [_a-z]+
464 ){{1,3}} File "<string>", line 23 in run
465 File ".*threading.py", line [0-9]+ in _bootstrap_inner
466 File ".*threading.py", line [0-9]+ in _bootstrap
Victor Stinner024e37a2011-03-31 01:31:06 +0200467
Victor Stinner6d201682014-08-10 19:50:08 +0200468 Current thread XXX \(most recent call first\):
469 File "<string>", line {lineno} in dump
470 File "<string>", line 28 in <module>$
471 """
472 regex = dedent(regex.format(lineno=lineno)).strip()
Victor Stinner024e37a2011-03-31 01:31:06 +0200473 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200474 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200475
476 def test_dump_traceback_threads(self):
477 self.check_dump_traceback_threads(None)
Victor Stinner19402332011-03-31 18:15:52 +0200478
479 def test_dump_traceback_threads_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200480 with temporary_filename() as filename:
481 self.check_dump_traceback_threads(filename)
482
Victor Stinner95bb7142015-03-12 15:32:03 +0100483 @unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'),
484 'need faulthandler.dump_traceback_later()')
485 def check_dump_traceback_later(self, repeat=False, cancel=False, loops=1,
486 *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200487 """
488 Check how many times the traceback is written in timeout x 2.5 seconds,
489 or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending
490 on repeat and cancel options.
491
492 Raise an error if the output doesn't match the expect format.
493 """
Victor Stinnerc790a532011-04-08 13:39:59 +0200494 timeout_str = str(datetime.timedelta(seconds=TIMEOUT))
Victor Stinner024e37a2011-03-31 01:31:06 +0200495 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200496 import faulthandler
497 import time
Victor Stinner95bb7142015-03-12 15:32:03 +0100498 import sys
499
500 timeout = {timeout}
501 repeat = {repeat}
502 cancel = {cancel}
503 loops = {loops}
504 filename = {filename!r}
505 fd = {fd}
Victor Stinner024e37a2011-03-31 01:31:06 +0200506
Victor Stinner6d201682014-08-10 19:50:08 +0200507 def func(timeout, repeat, cancel, file, loops):
508 for loop in range(loops):
509 faulthandler.dump_traceback_later(timeout, repeat=repeat, file=file)
510 if cancel:
511 faulthandler.cancel_dump_traceback_later()
512 time.sleep(timeout * 5)
513 faulthandler.cancel_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200514
Victor Stinner95bb7142015-03-12 15:32:03 +0100515 if filename:
516 file = open(filename, "wb")
517 elif fd is not None:
518 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200519 else:
520 file = None
521 func(timeout, repeat, cancel, file, loops)
Victor Stinner95bb7142015-03-12 15:32:03 +0100522 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200523 file.close()
524 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200525 code = code.format(
Victor Stinnerde10f402011-04-08 12:57:06 +0200526 timeout=TIMEOUT,
Victor Stinner024e37a2011-03-31 01:31:06 +0200527 repeat=repeat,
528 cancel=cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200529 loops=loops,
Victor Stinner95bb7142015-03-12 15:32:03 +0100530 filename=filename,
531 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200532 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200533 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200534 trace = '\n'.join(trace)
535
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200536 if not cancel:
Victor Stinnerde10f402011-04-08 12:57:06 +0200537 count = loops
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200538 if repeat:
Victor Stinnerde10f402011-04-08 12:57:06 +0200539 count *= 2
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700540 header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+ \(most recent call first\):\n' % timeout_str
Victor Stinner95bb7142015-03-12 15:32:03 +0100541 regex = expected_traceback(17, 26, header, min_count=count)
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200542 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200543 else:
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200544 self.assertEqual(trace, '')
Victor Stinner05585cb2011-03-31 13:29:56 +0200545 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200546
Georg Brandldeb92b52012-09-22 08:58:55 +0200547 def test_dump_traceback_later(self):
548 self.check_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200549
Georg Brandldeb92b52012-09-22 08:58:55 +0200550 def test_dump_traceback_later_repeat(self):
551 self.check_dump_traceback_later(repeat=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200552
Georg Brandldeb92b52012-09-22 08:58:55 +0200553 def test_dump_traceback_later_cancel(self):
554 self.check_dump_traceback_later(cancel=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200555
Georg Brandldeb92b52012-09-22 08:58:55 +0200556 def test_dump_traceback_later_file(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100557 with temporary_filename() as filename:
558 self.check_dump_traceback_later(filename=filename)
559
Victor Stinnerff2a6612015-03-13 11:01:30 +0100560 @unittest.skipIf(sys.platform == "win32",
561 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100562 def test_dump_traceback_later_fd(self):
563 with tempfile.TemporaryFile('wb+') as fp:
564 self.check_dump_traceback_later(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200565
Georg Brandldeb92b52012-09-22 08:58:55 +0200566 def test_dump_traceback_later_twice(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100567 self.check_dump_traceback_later(loops=2)
Victor Stinnerde10f402011-04-08 12:57:06 +0200568
Victor Stinner024e37a2011-03-31 01:31:06 +0200569 @unittest.skipIf(not hasattr(faulthandler, "register"),
570 "need faulthandler.register")
Victor Stinnera01ca122011-04-01 12:56:17 +0200571 def check_register(self, filename=False, all_threads=False,
Victor Stinner95bb7142015-03-12 15:32:03 +0100572 unregister=False, chain=False, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200573 """
574 Register a handler displaying the traceback on a user signal. Raise the
575 signal and check the written traceback.
576
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200577 If chain is True, check that the previous signal handler is called.
578
Victor Stinner024e37a2011-03-31 01:31:06 +0200579 Raise an error if the output doesn't match the expected format.
580 """
Victor Stinnera01ca122011-04-01 12:56:17 +0200581 signum = signal.SIGUSR1
Victor Stinner024e37a2011-03-31 01:31:06 +0200582 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200583 import faulthandler
584 import os
585 import signal
586 import sys
Victor Stinner024e37a2011-03-31 01:31:06 +0200587
Victor Stinner95bb7142015-03-12 15:32:03 +0100588 all_threads = {all_threads}
589 signum = {signum}
590 unregister = {unregister}
591 chain = {chain}
592 filename = {filename!r}
593 fd = {fd}
594
Victor Stinner6d201682014-08-10 19:50:08 +0200595 def func(signum):
596 os.kill(os.getpid(), signum)
Victor Stinner024e37a2011-03-31 01:31:06 +0200597
Victor Stinner6d201682014-08-10 19:50:08 +0200598 def handler(signum, frame):
599 handler.called = True
600 handler.called = False
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200601
Victor Stinner95bb7142015-03-12 15:32:03 +0100602 if filename:
603 file = open(filename, "wb")
604 elif fd is not None:
605 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200606 else:
607 file = None
608 if chain:
609 signal.signal(signum, handler)
610 faulthandler.register(signum, file=file,
Victor Stinner95bb7142015-03-12 15:32:03 +0100611 all_threads=all_threads, chain={chain})
Victor Stinner6d201682014-08-10 19:50:08 +0200612 if unregister:
613 faulthandler.unregister(signum)
614 func(signum)
615 if chain and not handler.called:
616 if file is not None:
617 output = file
618 else:
619 output = sys.stderr
620 print("Error: signal handler not called!", file=output)
621 exitcode = 1
Victor Stinner95bb7142015-03-12 15:32:03 +0100622 else:
623 exitcode = 0
624 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200625 file.close()
626 sys.exit(exitcode)
627 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200628 code = code.format(
Victor Stinner024e37a2011-03-31 01:31:06 +0200629 all_threads=all_threads,
Victor Stinnera01ca122011-04-01 12:56:17 +0200630 signum=signum,
631 unregister=unregister,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200632 chain=chain,
Victor Stinner95bb7142015-03-12 15:32:03 +0100633 filename=filename,
634 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200635 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200636 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200637 trace = '\n'.join(trace)
Victor Stinnera01ca122011-04-01 12:56:17 +0200638 if not unregister:
639 if all_threads:
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700640 regex = 'Current thread XXX \(most recent call first\):\n'
Victor Stinnera01ca122011-04-01 12:56:17 +0200641 else:
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700642 regex = 'Stack \(most recent call first\):\n'
Victor Stinner95bb7142015-03-12 15:32:03 +0100643 regex = expected_traceback(14, 32, regex)
Victor Stinnera01ca122011-04-01 12:56:17 +0200644 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200645 else:
Victor Stinnera01ca122011-04-01 12:56:17 +0200646 self.assertEqual(trace, '')
647 if unregister:
648 self.assertNotEqual(exitcode, 0)
649 else:
650 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200651
652 def test_register(self):
653 self.check_register()
654
Victor Stinnera01ca122011-04-01 12:56:17 +0200655 def test_unregister(self):
656 self.check_register(unregister=True)
657
Victor Stinner024e37a2011-03-31 01:31:06 +0200658 def test_register_file(self):
659 with temporary_filename() as filename:
660 self.check_register(filename=filename)
661
Victor Stinnerff2a6612015-03-13 11:01:30 +0100662 @unittest.skipIf(sys.platform == "win32",
663 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100664 def test_register_fd(self):
665 with tempfile.TemporaryFile('wb+') as fp:
666 self.check_register(fd=fp.fileno())
667
Victor Stinner024e37a2011-03-31 01:31:06 +0200668 def test_register_threads(self):
669 self.check_register(all_threads=True)
670
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200671 def test_register_chain(self):
672 self.check_register(chain=True)
673
Victor Stinnere2d66902014-05-14 17:15:50 +0200674 @contextmanager
675 def check_stderr_none(self):
676 stderr = sys.stderr
677 try:
678 sys.stderr = None
679 with self.assertRaises(RuntimeError) as cm:
680 yield
681 self.assertEqual(str(cm.exception), "sys.stderr is None")
682 finally:
683 sys.stderr = stderr
684
685 def test_stderr_None(self):
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +0300686 # Issue #21497: provide a helpful error if sys.stderr is None,
Victor Stinnere2d66902014-05-14 17:15:50 +0200687 # instead of just an attribute error: "None has no attribute fileno".
688 with self.check_stderr_none():
689 faulthandler.enable()
690 with self.check_stderr_none():
691 faulthandler.dump_traceback()
692 if hasattr(faulthandler, 'dump_traceback_later'):
693 with self.check_stderr_none():
694 faulthandler.dump_traceback_later(1e-3)
695 if hasattr(faulthandler, "register"):
696 with self.check_stderr_none():
697 faulthandler.register(signal.SIGUSR1)
698
Victor Stinner024e37a2011-03-31 01:31:06 +0200699
Victor Stinner024e37a2011-03-31 01:31:06 +0200700if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400701 unittest.main()