blob: 530b0931d3c39cd5a2d1a944e169f86f61f7f67d [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
9from test import support, script_helper
Victor Stinnerd5698cb2012-07-31 02:55:49 +020010from test.script_helper import assert_python_ok
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
Charles-François Natali3391e642011-09-01 23:08:21 +0200188 @unittest.skipIf(sys.platform.startswith('openbsd') and HAVE_THREADS,
189 "Issue #12868: sigaltstack() doesn't work on "
190 "OpenBSD if Python is compiled with pthread")
Victor Stinner024e37a2011-03-31 01:31:06 +0200191 @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'),
192 'need faulthandler._stack_overflow()')
193 def test_stack_overflow(self):
194 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200195 import faulthandler
196 faulthandler.enable()
197 faulthandler._stack_overflow()
198 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200199 3,
Victor Stinnerf0480752011-03-31 11:34:08 +0200200 '(?:Segmentation fault|Bus error)',
201 other_regex='unable to raise a stack overflow')
Victor Stinner024e37a2011-03-31 01:31:06 +0200202
203 def test_gil_released(self):
204 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200205 import faulthandler
206 faulthandler.enable()
Victor Stinner50838282014-09-30 13:54:14 +0200207 faulthandler._sigsegv(True)
Victor Stinner6d201682014-08-10 19:50:08 +0200208 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200209 3,
Victor Stinner50838282014-09-30 13:54:14 +0200210 'Segmentation fault')
Victor Stinner024e37a2011-03-31 01:31:06 +0200211
212 def test_enable_file(self):
213 with temporary_filename() as filename:
214 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200215 import faulthandler
216 output = open({filename}, 'wb')
217 faulthandler.enable(output)
218 faulthandler._sigsegv()
219 """.format(filename=repr(filename)),
Victor Stinner024e37a2011-03-31 01:31:06 +0200220 4,
Victor Stinner56785392013-06-17 23:37:59 +0200221 'Segmentation fault',
Victor Stinner024e37a2011-03-31 01:31:06 +0200222 filename=filename)
223
Victor Stinnerff2a6612015-03-13 11:01:30 +0100224 @unittest.skipIf(sys.platform == "win32",
225 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100226 def test_enable_fd(self):
227 with tempfile.TemporaryFile('wb+') as fp:
228 fd = fp.fileno()
229 self.check_fatal_error("""
230 import faulthandler
231 import sys
232 faulthandler.enable(%s)
233 faulthandler._sigsegv()
234 """ % fd,
235 4,
236 'Segmentation fault',
237 fd=fd)
238
Victor Stinner7bba62f2011-05-07 12:43:00 +0200239 def test_enable_single_thread(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200240 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200241 import faulthandler
242 faulthandler.enable(all_threads=False)
243 faulthandler._sigsegv()
244 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200245 3,
Victor Stinner56785392013-06-17 23:37:59 +0200246 'Segmentation fault',
Victor Stinner7bba62f2011-05-07 12:43:00 +0200247 all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200248
249 def test_disable(self):
250 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200251 import faulthandler
252 faulthandler.enable()
253 faulthandler.disable()
254 faulthandler._sigsegv()
255 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200256 not_expected = 'Fatal Python error'
Antoine Pitrou77e904e2013-10-08 23:04:32 +0200257 stderr, exitcode = self.get_output(code)
Victor Stinner29001c82014-09-25 00:38:48 +0200258 stderr = '\n'.join(stderr)
Victor Stinner024e37a2011-03-31 01:31:06 +0200259 self.assertTrue(not_expected not in stderr,
260 "%r is present in %r" % (not_expected, stderr))
Victor Stinner05585cb2011-03-31 13:29:56 +0200261 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200262
263 def test_is_enabled(self):
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200264 orig_stderr = sys.stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200265 try:
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200266 # regrtest may replace sys.stderr by io.StringIO object, but
267 # faulthandler.enable() requires that sys.stderr has a fileno()
268 # method
Victor Stinner72488502011-06-29 23:24:31 +0200269 sys.stderr = sys.__stderr__
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200270
271 was_enabled = faulthandler.is_enabled()
272 try:
Victor Stinner024e37a2011-03-31 01:31:06 +0200273 faulthandler.enable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200274 self.assertTrue(faulthandler.is_enabled())
Victor Stinner024e37a2011-03-31 01:31:06 +0200275 faulthandler.disable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200276 self.assertFalse(faulthandler.is_enabled())
277 finally:
278 if was_enabled:
279 faulthandler.enable()
280 else:
281 faulthandler.disable()
282 finally:
283 sys.stderr = orig_stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200284
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200285 def test_disabled_by_default(self):
286 # By default, the module should be disabled
287 code = "import faulthandler; print(faulthandler.is_enabled())"
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800288 args = filter(None, (sys.executable,
289 "-E" if sys.flags.ignore_environment else "",
290 "-c", code))
291 env = os.environ.copy()
292 env.pop("PYTHONFAULTHANDLER", None)
293 # don't use assert_python_ok() because it always enables faulthandler
294 output = subprocess.check_output(args, env=env)
Victor Stinner88983502013-09-08 11:36:23 +0200295 self.assertEqual(output.rstrip(), b"False")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200296
297 def test_sys_xoptions(self):
298 # Test python -X faulthandler
299 code = "import faulthandler; print(faulthandler.is_enabled())"
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800300 args = filter(None, (sys.executable,
301 "-E" if sys.flags.ignore_environment else "",
302 "-X", "faulthandler", "-c", code))
303 env = os.environ.copy()
304 env.pop("PYTHONFAULTHANDLER", None)
305 # don't use assert_python_ok() because it always enables faulthandler
306 output = subprocess.check_output(args, env=env)
Victor Stinner88983502013-09-08 11:36:23 +0200307 self.assertEqual(output.rstrip(), b"True")
308
309 def test_env_var(self):
310 # empty env var
311 code = "import faulthandler; print(faulthandler.is_enabled())"
312 args = (sys.executable, "-c", code)
313 env = os.environ.copy()
314 env['PYTHONFAULTHANDLER'] = ''
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800315 # don't use assert_python_ok() because it always enables faulthandler
Victor Stinner88983502013-09-08 11:36:23 +0200316 output = subprocess.check_output(args, env=env)
317 self.assertEqual(output.rstrip(), b"False")
318
319 # non-empty env var
320 env = os.environ.copy()
321 env['PYTHONFAULTHANDLER'] = '1'
322 output = subprocess.check_output(args, env=env)
323 self.assertEqual(output.rstrip(), b"True")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200324
Victor Stinner95bb7142015-03-12 15:32:03 +0100325 def check_dump_traceback(self, *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200326 """
327 Explicitly call dump_traceback() function and check its output.
328 Raise an error if the output doesn't match the expected format.
329 """
330 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200331 import faulthandler
Victor Stinner024e37a2011-03-31 01:31:06 +0200332
Victor Stinner95bb7142015-03-12 15:32:03 +0100333 filename = {filename!r}
334 fd = {fd}
335
Victor Stinner6d201682014-08-10 19:50:08 +0200336 def funcB():
Victor Stinner95bb7142015-03-12 15:32:03 +0100337 if filename:
338 with open(filename, "wb") as fp:
Victor Stinner6d201682014-08-10 19:50:08 +0200339 faulthandler.dump_traceback(fp, all_threads=False)
Victor Stinner95bb7142015-03-12 15:32:03 +0100340 elif fd is not None:
341 faulthandler.dump_traceback(fd,
342 all_threads=False)
Victor Stinner6d201682014-08-10 19:50:08 +0200343 else:
344 faulthandler.dump_traceback(all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200345
Victor Stinner6d201682014-08-10 19:50:08 +0200346 def funcA():
347 funcB()
Victor Stinner024e37a2011-03-31 01:31:06 +0200348
Victor Stinner6d201682014-08-10 19:50:08 +0200349 funcA()
350 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200351 code = code.format(
Victor Stinner95bb7142015-03-12 15:32:03 +0100352 filename=filename,
353 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200354 )
355 if filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100356 lineno = 9
357 elif fd is not None:
358 lineno = 12
Victor Stinner024e37a2011-03-31 01:31:06 +0200359 else:
Victor Stinner95bb7142015-03-12 15:32:03 +0100360 lineno = 14
Victor Stinner024e37a2011-03-31 01:31:06 +0200361 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700362 'Stack (most recent call first):',
Victor Stinner024e37a2011-03-31 01:31:06 +0200363 ' File "<string>", line %s in funcB' % lineno,
Victor Stinner95bb7142015-03-12 15:32:03 +0100364 ' File "<string>", line 17 in funcA',
365 ' File "<string>", line 19 in <module>'
Victor Stinner024e37a2011-03-31 01:31:06 +0200366 ]
Victor Stinner95bb7142015-03-12 15:32:03 +0100367 trace, exitcode = self.get_output(code, filename, fd)
Victor Stinner024e37a2011-03-31 01:31:06 +0200368 self.assertEqual(trace, expected)
Victor Stinner05585cb2011-03-31 13:29:56 +0200369 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200370
371 def test_dump_traceback(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100372 self.check_dump_traceback()
Victor Stinner19402332011-03-31 18:15:52 +0200373
374 def test_dump_traceback_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200375 with temporary_filename() as filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100376 self.check_dump_traceback(filename=filename)
377
Victor Stinnerff2a6612015-03-13 11:01:30 +0100378 @unittest.skipIf(sys.platform == "win32",
379 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100380 def test_dump_traceback_fd(self):
381 with tempfile.TemporaryFile('wb+') as fp:
382 self.check_dump_traceback(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200383
Victor Stinner53386d82012-08-01 19:45:34 +0200384 def test_truncate(self):
385 maxlen = 500
386 func_name = 'x' * (maxlen + 50)
387 truncated = 'x' * maxlen + '...'
388 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200389 import faulthandler
Victor Stinner53386d82012-08-01 19:45:34 +0200390
Victor Stinner6d201682014-08-10 19:50:08 +0200391 def {func_name}():
392 faulthandler.dump_traceback(all_threads=False)
Victor Stinner53386d82012-08-01 19:45:34 +0200393
Victor Stinner6d201682014-08-10 19:50:08 +0200394 {func_name}()
395 """
Victor Stinner53386d82012-08-01 19:45:34 +0200396 code = code.format(
397 func_name=func_name,
398 )
399 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700400 'Stack (most recent call first):',
Victor Stinner53386d82012-08-01 19:45:34 +0200401 ' File "<string>", line 4 in %s' % truncated,
402 ' File "<string>", line 6 in <module>'
403 ]
404 trace, exitcode = self.get_output(code)
405 self.assertEqual(trace, expected)
406 self.assertEqual(exitcode, 0)
407
Victor Stinnerff4cd882011-04-07 11:50:25 +0200408 @unittest.skipIf(not HAVE_THREADS, 'need threads')
Victor Stinner024e37a2011-03-31 01:31:06 +0200409 def check_dump_traceback_threads(self, filename):
410 """
411 Call explicitly dump_traceback(all_threads=True) and check the output.
412 Raise an error if the output doesn't match the expected format.
413 """
414 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200415 import faulthandler
416 from threading import Thread, Event
417 import time
Victor Stinner024e37a2011-03-31 01:31:06 +0200418
Victor Stinner6d201682014-08-10 19:50:08 +0200419 def dump():
420 if {filename}:
421 with open({filename}, "wb") as fp:
422 faulthandler.dump_traceback(fp, all_threads=True)
423 else:
424 faulthandler.dump_traceback(all_threads=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200425
Victor Stinner6d201682014-08-10 19:50:08 +0200426 class Waiter(Thread):
427 # avoid blocking if the main thread raises an exception.
428 daemon = True
Victor Stinner024e37a2011-03-31 01:31:06 +0200429
Victor Stinner6d201682014-08-10 19:50:08 +0200430 def __init__(self):
431 Thread.__init__(self)
432 self.running = Event()
433 self.stop = Event()
Victor Stinner024e37a2011-03-31 01:31:06 +0200434
Victor Stinner6d201682014-08-10 19:50:08 +0200435 def run(self):
436 self.running.set()
437 self.stop.wait()
Victor Stinner024e37a2011-03-31 01:31:06 +0200438
Victor Stinner6d201682014-08-10 19:50:08 +0200439 waiter = Waiter()
440 waiter.start()
441 waiter.running.wait()
442 dump()
443 waiter.stop.set()
444 waiter.join()
445 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200446 code = code.format(filename=repr(filename))
Victor Stinner05585cb2011-03-31 13:29:56 +0200447 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200448 output = '\n'.join(output)
449 if filename:
450 lineno = 8
451 else:
452 lineno = 10
453 regex = """
Victor Stinner6d201682014-08-10 19:50:08 +0200454 ^Thread 0x[0-9a-f]+ \(most recent call first\):
455 (?: File ".*threading.py", line [0-9]+ in [_a-z]+
456 ){{1,3}} File "<string>", line 23 in run
457 File ".*threading.py", line [0-9]+ in _bootstrap_inner
458 File ".*threading.py", line [0-9]+ in _bootstrap
Victor Stinner024e37a2011-03-31 01:31:06 +0200459
Victor Stinner6d201682014-08-10 19:50:08 +0200460 Current thread XXX \(most recent call first\):
461 File "<string>", line {lineno} in dump
462 File "<string>", line 28 in <module>$
463 """
464 regex = dedent(regex.format(lineno=lineno)).strip()
Victor Stinner024e37a2011-03-31 01:31:06 +0200465 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200466 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200467
468 def test_dump_traceback_threads(self):
469 self.check_dump_traceback_threads(None)
Victor Stinner19402332011-03-31 18:15:52 +0200470
471 def test_dump_traceback_threads_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200472 with temporary_filename() as filename:
473 self.check_dump_traceback_threads(filename)
474
Victor Stinner95bb7142015-03-12 15:32:03 +0100475 @unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'),
476 'need faulthandler.dump_traceback_later()')
477 def check_dump_traceback_later(self, repeat=False, cancel=False, loops=1,
478 *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200479 """
480 Check how many times the traceback is written in timeout x 2.5 seconds,
481 or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending
482 on repeat and cancel options.
483
484 Raise an error if the output doesn't match the expect format.
485 """
Victor Stinnerc790a532011-04-08 13:39:59 +0200486 timeout_str = str(datetime.timedelta(seconds=TIMEOUT))
Victor Stinner024e37a2011-03-31 01:31:06 +0200487 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200488 import faulthandler
489 import time
Victor Stinner95bb7142015-03-12 15:32:03 +0100490 import sys
491
492 timeout = {timeout}
493 repeat = {repeat}
494 cancel = {cancel}
495 loops = {loops}
496 filename = {filename!r}
497 fd = {fd}
Victor Stinner024e37a2011-03-31 01:31:06 +0200498
Victor Stinner6d201682014-08-10 19:50:08 +0200499 def func(timeout, repeat, cancel, file, loops):
500 for loop in range(loops):
501 faulthandler.dump_traceback_later(timeout, repeat=repeat, file=file)
502 if cancel:
503 faulthandler.cancel_dump_traceback_later()
504 time.sleep(timeout * 5)
505 faulthandler.cancel_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200506
Victor Stinner95bb7142015-03-12 15:32:03 +0100507 if filename:
508 file = open(filename, "wb")
509 elif fd is not None:
510 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200511 else:
512 file = None
513 func(timeout, repeat, cancel, file, loops)
Victor Stinner95bb7142015-03-12 15:32:03 +0100514 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200515 file.close()
516 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200517 code = code.format(
Victor Stinnerde10f402011-04-08 12:57:06 +0200518 timeout=TIMEOUT,
Victor Stinner024e37a2011-03-31 01:31:06 +0200519 repeat=repeat,
520 cancel=cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200521 loops=loops,
Victor Stinner95bb7142015-03-12 15:32:03 +0100522 filename=filename,
523 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200524 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200525 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200526 trace = '\n'.join(trace)
527
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200528 if not cancel:
Victor Stinnerde10f402011-04-08 12:57:06 +0200529 count = loops
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200530 if repeat:
Victor Stinnerde10f402011-04-08 12:57:06 +0200531 count *= 2
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700532 header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+ \(most recent call first\):\n' % timeout_str
Victor Stinner95bb7142015-03-12 15:32:03 +0100533 regex = expected_traceback(17, 26, header, min_count=count)
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200534 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200535 else:
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200536 self.assertEqual(trace, '')
Victor Stinner05585cb2011-03-31 13:29:56 +0200537 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200538
Georg Brandldeb92b52012-09-22 08:58:55 +0200539 def test_dump_traceback_later(self):
540 self.check_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200541
Georg Brandldeb92b52012-09-22 08:58:55 +0200542 def test_dump_traceback_later_repeat(self):
543 self.check_dump_traceback_later(repeat=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200544
Georg Brandldeb92b52012-09-22 08:58:55 +0200545 def test_dump_traceback_later_cancel(self):
546 self.check_dump_traceback_later(cancel=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200547
Georg Brandldeb92b52012-09-22 08:58:55 +0200548 def test_dump_traceback_later_file(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100549 with temporary_filename() as filename:
550 self.check_dump_traceback_later(filename=filename)
551
Victor Stinnerff2a6612015-03-13 11:01:30 +0100552 @unittest.skipIf(sys.platform == "win32",
553 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100554 def test_dump_traceback_later_fd(self):
555 with tempfile.TemporaryFile('wb+') as fp:
556 self.check_dump_traceback_later(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200557
Georg Brandldeb92b52012-09-22 08:58:55 +0200558 def test_dump_traceback_later_twice(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100559 self.check_dump_traceback_later(loops=2)
Victor Stinnerde10f402011-04-08 12:57:06 +0200560
Victor Stinner024e37a2011-03-31 01:31:06 +0200561 @unittest.skipIf(not hasattr(faulthandler, "register"),
562 "need faulthandler.register")
Victor Stinnera01ca122011-04-01 12:56:17 +0200563 def check_register(self, filename=False, all_threads=False,
Victor Stinner95bb7142015-03-12 15:32:03 +0100564 unregister=False, chain=False, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200565 """
566 Register a handler displaying the traceback on a user signal. Raise the
567 signal and check the written traceback.
568
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200569 If chain is True, check that the previous signal handler is called.
570
Victor Stinner024e37a2011-03-31 01:31:06 +0200571 Raise an error if the output doesn't match the expected format.
572 """
Victor Stinnera01ca122011-04-01 12:56:17 +0200573 signum = signal.SIGUSR1
Victor Stinner024e37a2011-03-31 01:31:06 +0200574 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200575 import faulthandler
576 import os
577 import signal
578 import sys
Victor Stinner024e37a2011-03-31 01:31:06 +0200579
Victor Stinner95bb7142015-03-12 15:32:03 +0100580 all_threads = {all_threads}
581 signum = {signum}
582 unregister = {unregister}
583 chain = {chain}
584 filename = {filename!r}
585 fd = {fd}
586
Victor Stinner6d201682014-08-10 19:50:08 +0200587 def func(signum):
588 os.kill(os.getpid(), signum)
Victor Stinner024e37a2011-03-31 01:31:06 +0200589
Victor Stinner6d201682014-08-10 19:50:08 +0200590 def handler(signum, frame):
591 handler.called = True
592 handler.called = False
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200593
Victor Stinner95bb7142015-03-12 15:32:03 +0100594 if filename:
595 file = open(filename, "wb")
596 elif fd is not None:
597 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200598 else:
599 file = None
600 if chain:
601 signal.signal(signum, handler)
602 faulthandler.register(signum, file=file,
Victor Stinner95bb7142015-03-12 15:32:03 +0100603 all_threads=all_threads, chain={chain})
Victor Stinner6d201682014-08-10 19:50:08 +0200604 if unregister:
605 faulthandler.unregister(signum)
606 func(signum)
607 if chain and not handler.called:
608 if file is not None:
609 output = file
610 else:
611 output = sys.stderr
612 print("Error: signal handler not called!", file=output)
613 exitcode = 1
Victor Stinner95bb7142015-03-12 15:32:03 +0100614 else:
615 exitcode = 0
616 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200617 file.close()
618 sys.exit(exitcode)
619 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200620 code = code.format(
Victor Stinner024e37a2011-03-31 01:31:06 +0200621 all_threads=all_threads,
Victor Stinnera01ca122011-04-01 12:56:17 +0200622 signum=signum,
623 unregister=unregister,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200624 chain=chain,
Victor Stinner95bb7142015-03-12 15:32:03 +0100625 filename=filename,
626 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200627 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200628 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200629 trace = '\n'.join(trace)
Victor Stinnera01ca122011-04-01 12:56:17 +0200630 if not unregister:
631 if all_threads:
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700632 regex = 'Current thread XXX \(most recent call first\):\n'
Victor Stinnera01ca122011-04-01 12:56:17 +0200633 else:
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700634 regex = 'Stack \(most recent call first\):\n'
Victor Stinner95bb7142015-03-12 15:32:03 +0100635 regex = expected_traceback(14, 32, regex)
Victor Stinnera01ca122011-04-01 12:56:17 +0200636 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200637 else:
Victor Stinnera01ca122011-04-01 12:56:17 +0200638 self.assertEqual(trace, '')
639 if unregister:
640 self.assertNotEqual(exitcode, 0)
641 else:
642 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200643
644 def test_register(self):
645 self.check_register()
646
Victor Stinnera01ca122011-04-01 12:56:17 +0200647 def test_unregister(self):
648 self.check_register(unregister=True)
649
Victor Stinner024e37a2011-03-31 01:31:06 +0200650 def test_register_file(self):
651 with temporary_filename() as filename:
652 self.check_register(filename=filename)
653
Victor Stinnerff2a6612015-03-13 11:01:30 +0100654 @unittest.skipIf(sys.platform == "win32",
655 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100656 def test_register_fd(self):
657 with tempfile.TemporaryFile('wb+') as fp:
658 self.check_register(fd=fp.fileno())
659
Victor Stinner024e37a2011-03-31 01:31:06 +0200660 def test_register_threads(self):
661 self.check_register(all_threads=True)
662
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200663 def test_register_chain(self):
664 self.check_register(chain=True)
665
Victor Stinnere2d66902014-05-14 17:15:50 +0200666 @contextmanager
667 def check_stderr_none(self):
668 stderr = sys.stderr
669 try:
670 sys.stderr = None
671 with self.assertRaises(RuntimeError) as cm:
672 yield
673 self.assertEqual(str(cm.exception), "sys.stderr is None")
674 finally:
675 sys.stderr = stderr
676
677 def test_stderr_None(self):
678 # Issue #21497: provide an helpful error if sys.stderr is None,
679 # instead of just an attribute error: "None has no attribute fileno".
680 with self.check_stderr_none():
681 faulthandler.enable()
682 with self.check_stderr_none():
683 faulthandler.dump_traceback()
684 if hasattr(faulthandler, 'dump_traceback_later'):
685 with self.check_stderr_none():
686 faulthandler.dump_traceback_later(1e-3)
687 if hasattr(faulthandler, "register"):
688 with self.check_stderr_none():
689 faulthandler.register(signal.SIGUSR1)
690
Victor Stinner024e37a2011-03-31 01:31:06 +0200691
Victor Stinner024e37a2011-03-31 01:31:06 +0200692if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400693 unittest.main()