blob: d2bd2d21e87544c12bbba948524b0a9dbde81009 [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
Victor Stinner404cdc52016-03-23 10:39:17 +010026MS_WINDOWS = (os.name == 'nt')
Victor Stinner44378d42011-04-01 15:37:12 +020027
Victor Stinner301f3f02011-06-01 13:49:12 +020028def expected_traceback(lineno1, lineno2, header, min_count=1):
Victor Stinner024e37a2011-03-31 01:31:06 +020029 regex = header
Victor Stinner7ad24e92011-03-31 22:35:49 +020030 regex += ' File "<string>", line %s in func\n' % lineno1
31 regex += ' File "<string>", line %s in <module>' % lineno2
Victor Stinner301f3f02011-06-01 13:49:12 +020032 if 1 < min_count:
33 return '^' + (regex + '\n') * (min_count - 1) + regex
34 else:
35 return '^' + regex + '$'
Victor Stinner024e37a2011-03-31 01:31:06 +020036
37@contextmanager
38def temporary_filename():
39 filename = tempfile.mktemp()
40 try:
41 yield filename
42 finally:
43 support.unlink(filename)
44
45class FaultHandlerTests(unittest.TestCase):
Victor Stinner95bb7142015-03-12 15:32:03 +010046 def get_output(self, code, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020047 """
48 Run the specified code in Python (in a new child process) and read the
49 output from the standard error or from a file (if filename is set).
50 Return the output lines as a list.
51
52 Strip the reference count from the standard error for Python debug
53 build, and replace "Current thread 0x00007f8d8fbd9700" by "Current
54 thread XXX".
55 """
Victor Stinner6d201682014-08-10 19:50:08 +020056 code = dedent(code).strip()
Victor Stinner95bb7142015-03-12 15:32:03 +010057 pass_fds = []
58 if fd is not None:
59 pass_fds.append(fd)
Antoine Pitrou77e904e2013-10-08 23:04:32 +020060 with support.SuppressCrashReport():
Victor Stinner95bb7142015-03-12 15:32:03 +010061 process = script_helper.spawn_python('-c', code, pass_fds=pass_fds)
Victor Stinner861d9ab2016-03-16 22:45:24 +010062 with process:
63 stdout, stderr = process.communicate()
64 exitcode = process.wait()
Victor Stinner19402332011-03-31 18:15:52 +020065 output = support.strip_python_stderr(stdout)
66 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020067 if filename:
Victor Stinner19402332011-03-31 18:15:52 +020068 self.assertEqual(output, '')
Victor Stinner024e37a2011-03-31 01:31:06 +020069 with open(filename, "rb") as fp:
70 output = fp.read()
Victor Stinner19402332011-03-31 18:15:52 +020071 output = output.decode('ascii', 'backslashreplace')
Victor Stinner95bb7142015-03-12 15:32:03 +010072 elif fd is not None:
73 self.assertEqual(output, '')
74 os.lseek(fd, os.SEEK_SET, 0)
75 with open(fd, "rb", closefd=False) as fp:
76 output = fp.read()
77 output = output.decode('ascii', 'backslashreplace')
Victor Stinner05585cb2011-03-31 13:29:56 +020078 return output.splitlines(), exitcode
Victor Stinner024e37a2011-03-31 01:31:06 +020079
Victor Stinner404cdc52016-03-23 10:39:17 +010080 def check_error(self, code, line_number, fatal_error, *,
81 filename=None, all_threads=True, other_regex=None,
82 fd=None, know_current_thread=True):
Victor Stinner024e37a2011-03-31 01:31:06 +020083 """
84 Check that the fault handler for fatal errors is enabled and check the
85 traceback from the child process output.
86
87 Raise an error if the output doesn't match the expected format.
88 """
89 if all_threads:
Victor Stinner861d9ab2016-03-16 22:45:24 +010090 if know_current_thread:
91 header = 'Current thread 0x[0-9a-f]+'
92 else:
93 header = 'Thread 0x[0-9a-f]+'
Victor Stinner024e37a2011-03-31 01:31:06 +020094 else:
Victor Stinner861d9ab2016-03-16 22:45:24 +010095 header = 'Stack'
R David Murray44b548d2016-09-08 13:59:53 -040096 regex = r"""
Victor Stinner404cdc52016-03-23 10:39:17 +010097 ^{fatal_error}
Victor Stinner024e37a2011-03-31 01:31:06 +020098
Victor Stinner861d9ab2016-03-16 22:45:24 +010099 {header} \(most recent call first\):
Victor Stinner6d201682014-08-10 19:50:08 +0200100 File "<string>", line {lineno} in <module>
101 """
102 regex = dedent(regex.format(
Victor Stinner024e37a2011-03-31 01:31:06 +0200103 lineno=line_number,
Victor Stinner404cdc52016-03-23 10:39:17 +0100104 fatal_error=fatal_error,
Victor Stinner861d9ab2016-03-16 22:45:24 +0100105 header=header)).strip()
Victor Stinnerf0480752011-03-31 11:34:08 +0200106 if other_regex:
107 regex += '|' + other_regex
Victor Stinner95bb7142015-03-12 15:32:03 +0100108 output, exitcode = self.get_output(code, filename=filename, fd=fd)
Victor Stinner024e37a2011-03-31 01:31:06 +0200109 output = '\n'.join(output)
110 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200111 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200112
Victor Stinner404cdc52016-03-23 10:39:17 +0100113 def check_fatal_error(self, code, line_number, name_regex, **kw):
114 fatal_error = 'Fatal Python error: %s' % name_regex
115 self.check_error(code, line_number, fatal_error, **kw)
116
117 def check_windows_exception(self, code, line_number, name_regex, **kw):
Victor Stinner412a5e72016-03-23 14:44:14 +0100118 fatal_error = 'Windows fatal exception: %s' % name_regex
Victor Stinner404cdc52016-03-23 10:39:17 +0100119 self.check_error(code, line_number, fatal_error, **kw)
120
Victor Stinner330426c2013-07-03 22:29:42 +0200121 @unittest.skipIf(sys.platform.startswith('aix'),
122 "the first page of memory is a mapped read-only on AIX")
Victor Stinner024e37a2011-03-31 01:31:06 +0200123 def test_read_null(self):
Victor Stinner404cdc52016-03-23 10:39:17 +0100124 if not MS_WINDOWS:
125 self.check_fatal_error("""
126 import faulthandler
127 faulthandler.enable()
128 faulthandler._read_null()
129 """,
130 3,
131 # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion
132 '(?:Segmentation fault'
133 '|Bus error'
134 '|Illegal instruction)')
135 else:
136 self.check_windows_exception("""
137 import faulthandler
138 faulthandler.enable()
139 faulthandler._read_null()
140 """,
141 3,
142 'access violation')
Victor Stinner024e37a2011-03-31 01:31:06 +0200143
144 def test_sigsegv(self):
145 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200146 import faulthandler
147 faulthandler.enable()
148 faulthandler._sigsegv()
149 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200150 3,
151 'Segmentation fault')
152
Victor Stinner861d9ab2016-03-16 22:45:24 +0100153 @unittest.skipIf(not HAVE_THREADS, 'need threads')
154 def test_fatal_error_c_thread(self):
155 self.check_fatal_error("""
156 import faulthandler
157 faulthandler.enable()
158 faulthandler._fatal_error_c_thread()
159 """,
160 3,
161 'in new thread',
162 know_current_thread=False)
163
Victor Stinnerd727e232011-04-01 12:13:55 +0200164 def test_sigabrt(self):
165 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200166 import faulthandler
167 faulthandler.enable()
168 faulthandler._sigabrt()
169 """,
Victor Stinnerd727e232011-04-01 12:13:55 +0200170 3,
171 'Aborted')
172
Victor Stinner024e37a2011-03-31 01:31:06 +0200173 @unittest.skipIf(sys.platform == 'win32',
174 "SIGFPE cannot be caught on Windows")
175 def test_sigfpe(self):
176 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200177 import faulthandler
178 faulthandler.enable()
179 faulthandler._sigfpe()
180 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200181 3,
182 'Floating point exception')
183
Victor Stinner56e8c292014-07-21 12:30:22 +0200184 @unittest.skipIf(_testcapi is None, 'need _testcapi')
185 @unittest.skipUnless(hasattr(signal, 'SIGBUS'), 'need signal.SIGBUS')
Victor Stinner024e37a2011-03-31 01:31:06 +0200186 def test_sigbus(self):
187 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200188 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200189 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200190 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200191
Victor Stinner6d201682014-08-10 19:50:08 +0200192 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200193 _testcapi.raise_signal(signal.SIGBUS)
Victor Stinner6d201682014-08-10 19:50:08 +0200194 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200195 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200196 'Bus error')
197
Victor Stinner56e8c292014-07-21 12:30:22 +0200198 @unittest.skipIf(_testcapi is None, 'need _testcapi')
199 @unittest.skipUnless(hasattr(signal, 'SIGILL'), 'need signal.SIGILL')
Victor Stinner024e37a2011-03-31 01:31:06 +0200200 def test_sigill(self):
201 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200202 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200203 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200204 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200205
Victor Stinner6d201682014-08-10 19:50:08 +0200206 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200207 _testcapi.raise_signal(signal.SIGILL)
Victor Stinner6d201682014-08-10 19:50:08 +0200208 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200209 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200210 'Illegal instruction')
211
212 def test_fatal_error(self):
213 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200214 import faulthandler
215 faulthandler._fatal_error(b'xyz')
216 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200217 2,
218 'xyz')
219
Victor Stinner57003f82016-03-15 17:23:35 +0100220 def test_fatal_error_without_gil(self):
221 self.check_fatal_error("""
222 import faulthandler
223 faulthandler._fatal_error(b'xyz', True)
224 """,
225 2,
226 'xyz')
227
Charles-François Natali3391e642011-09-01 23:08:21 +0200228 @unittest.skipIf(sys.platform.startswith('openbsd') and HAVE_THREADS,
229 "Issue #12868: sigaltstack() doesn't work on "
230 "OpenBSD if Python is compiled with pthread")
Victor Stinner024e37a2011-03-31 01:31:06 +0200231 @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'),
232 'need faulthandler._stack_overflow()')
233 def test_stack_overflow(self):
234 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200235 import faulthandler
236 faulthandler.enable()
237 faulthandler._stack_overflow()
238 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200239 3,
Victor Stinnerf0480752011-03-31 11:34:08 +0200240 '(?:Segmentation fault|Bus error)',
241 other_regex='unable to raise a stack overflow')
Victor Stinner024e37a2011-03-31 01:31:06 +0200242
243 def test_gil_released(self):
244 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200245 import faulthandler
246 faulthandler.enable()
Victor Stinner50838282014-09-30 13:54:14 +0200247 faulthandler._sigsegv(True)
Victor Stinner6d201682014-08-10 19:50:08 +0200248 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200249 3,
Victor Stinner50838282014-09-30 13:54:14 +0200250 'Segmentation fault')
Victor Stinner024e37a2011-03-31 01:31:06 +0200251
252 def test_enable_file(self):
253 with temporary_filename() as filename:
254 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200255 import faulthandler
256 output = open({filename}, 'wb')
257 faulthandler.enable(output)
258 faulthandler._sigsegv()
259 """.format(filename=repr(filename)),
Victor Stinner024e37a2011-03-31 01:31:06 +0200260 4,
Victor Stinner56785392013-06-17 23:37:59 +0200261 'Segmentation fault',
Victor Stinner024e37a2011-03-31 01:31:06 +0200262 filename=filename)
263
Victor Stinnerff2a6612015-03-13 11:01:30 +0100264 @unittest.skipIf(sys.platform == "win32",
265 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100266 def test_enable_fd(self):
267 with tempfile.TemporaryFile('wb+') as fp:
268 fd = fp.fileno()
269 self.check_fatal_error("""
270 import faulthandler
271 import sys
272 faulthandler.enable(%s)
273 faulthandler._sigsegv()
274 """ % fd,
275 4,
276 'Segmentation fault',
277 fd=fd)
278
Victor Stinner7bba62f2011-05-07 12:43:00 +0200279 def test_enable_single_thread(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200280 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200281 import faulthandler
282 faulthandler.enable(all_threads=False)
283 faulthandler._sigsegv()
284 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200285 3,
Victor Stinner56785392013-06-17 23:37:59 +0200286 'Segmentation fault',
Victor Stinner7bba62f2011-05-07 12:43:00 +0200287 all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200288
289 def test_disable(self):
290 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200291 import faulthandler
292 faulthandler.enable()
293 faulthandler.disable()
294 faulthandler._sigsegv()
295 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200296 not_expected = 'Fatal Python error'
Antoine Pitrou77e904e2013-10-08 23:04:32 +0200297 stderr, exitcode = self.get_output(code)
Victor Stinner29001c82014-09-25 00:38:48 +0200298 stderr = '\n'.join(stderr)
Victor Stinner024e37a2011-03-31 01:31:06 +0200299 self.assertTrue(not_expected not in stderr,
300 "%r is present in %r" % (not_expected, stderr))
Victor Stinner05585cb2011-03-31 13:29:56 +0200301 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200302
303 def test_is_enabled(self):
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200304 orig_stderr = sys.stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200305 try:
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200306 # regrtest may replace sys.stderr by io.StringIO object, but
307 # faulthandler.enable() requires that sys.stderr has a fileno()
308 # method
Victor Stinner72488502011-06-29 23:24:31 +0200309 sys.stderr = sys.__stderr__
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200310
311 was_enabled = faulthandler.is_enabled()
312 try:
Victor Stinner024e37a2011-03-31 01:31:06 +0200313 faulthandler.enable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200314 self.assertTrue(faulthandler.is_enabled())
Victor Stinner024e37a2011-03-31 01:31:06 +0200315 faulthandler.disable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200316 self.assertFalse(faulthandler.is_enabled())
317 finally:
318 if was_enabled:
319 faulthandler.enable()
320 else:
321 faulthandler.disable()
322 finally:
323 sys.stderr = orig_stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200324
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200325 def test_disabled_by_default(self):
326 # By default, the module should be disabled
327 code = "import faulthandler; print(faulthandler.is_enabled())"
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800328 args = filter(None, (sys.executable,
329 "-E" if sys.flags.ignore_environment else "",
330 "-c", code))
331 env = os.environ.copy()
332 env.pop("PYTHONFAULTHANDLER", None)
333 # don't use assert_python_ok() because it always enables faulthandler
334 output = subprocess.check_output(args, env=env)
Victor Stinner88983502013-09-08 11:36:23 +0200335 self.assertEqual(output.rstrip(), b"False")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200336
337 def test_sys_xoptions(self):
338 # Test python -X faulthandler
339 code = "import faulthandler; print(faulthandler.is_enabled())"
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800340 args = filter(None, (sys.executable,
341 "-E" if sys.flags.ignore_environment else "",
342 "-X", "faulthandler", "-c", code))
343 env = os.environ.copy()
344 env.pop("PYTHONFAULTHANDLER", None)
345 # don't use assert_python_ok() because it always enables faulthandler
346 output = subprocess.check_output(args, env=env)
Victor Stinner88983502013-09-08 11:36:23 +0200347 self.assertEqual(output.rstrip(), b"True")
348
349 def test_env_var(self):
350 # empty env var
351 code = "import faulthandler; print(faulthandler.is_enabled())"
352 args = (sys.executable, "-c", code)
353 env = os.environ.copy()
354 env['PYTHONFAULTHANDLER'] = ''
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800355 # don't use assert_python_ok() because it always enables faulthandler
Victor Stinner88983502013-09-08 11:36:23 +0200356 output = subprocess.check_output(args, env=env)
357 self.assertEqual(output.rstrip(), b"False")
358
359 # non-empty env var
360 env = os.environ.copy()
361 env['PYTHONFAULTHANDLER'] = '1'
362 output = subprocess.check_output(args, env=env)
363 self.assertEqual(output.rstrip(), b"True")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200364
Victor Stinner95bb7142015-03-12 15:32:03 +0100365 def check_dump_traceback(self, *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200366 """
367 Explicitly call dump_traceback() function and check its output.
368 Raise an error if the output doesn't match the expected format.
369 """
370 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200371 import faulthandler
Victor Stinner024e37a2011-03-31 01:31:06 +0200372
Victor Stinner95bb7142015-03-12 15:32:03 +0100373 filename = {filename!r}
374 fd = {fd}
375
Victor Stinner6d201682014-08-10 19:50:08 +0200376 def funcB():
Victor Stinner95bb7142015-03-12 15:32:03 +0100377 if filename:
378 with open(filename, "wb") as fp:
Victor Stinner6d201682014-08-10 19:50:08 +0200379 faulthandler.dump_traceback(fp, all_threads=False)
Victor Stinner95bb7142015-03-12 15:32:03 +0100380 elif fd is not None:
381 faulthandler.dump_traceback(fd,
382 all_threads=False)
Victor Stinner6d201682014-08-10 19:50:08 +0200383 else:
384 faulthandler.dump_traceback(all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200385
Victor Stinner6d201682014-08-10 19:50:08 +0200386 def funcA():
387 funcB()
Victor Stinner024e37a2011-03-31 01:31:06 +0200388
Victor Stinner6d201682014-08-10 19:50:08 +0200389 funcA()
390 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200391 code = code.format(
Victor Stinner95bb7142015-03-12 15:32:03 +0100392 filename=filename,
393 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200394 )
395 if filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100396 lineno = 9
397 elif fd is not None:
398 lineno = 12
Victor Stinner024e37a2011-03-31 01:31:06 +0200399 else:
Victor Stinner95bb7142015-03-12 15:32:03 +0100400 lineno = 14
Victor Stinner024e37a2011-03-31 01:31:06 +0200401 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700402 'Stack (most recent call first):',
Victor Stinner024e37a2011-03-31 01:31:06 +0200403 ' File "<string>", line %s in funcB' % lineno,
Victor Stinner95bb7142015-03-12 15:32:03 +0100404 ' File "<string>", line 17 in funcA',
405 ' File "<string>", line 19 in <module>'
Victor Stinner024e37a2011-03-31 01:31:06 +0200406 ]
Victor Stinner95bb7142015-03-12 15:32:03 +0100407 trace, exitcode = self.get_output(code, filename, fd)
Victor Stinner024e37a2011-03-31 01:31:06 +0200408 self.assertEqual(trace, expected)
Victor Stinner05585cb2011-03-31 13:29:56 +0200409 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200410
411 def test_dump_traceback(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100412 self.check_dump_traceback()
Victor Stinner19402332011-03-31 18:15:52 +0200413
414 def test_dump_traceback_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200415 with temporary_filename() as filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100416 self.check_dump_traceback(filename=filename)
417
Victor Stinnerff2a6612015-03-13 11:01:30 +0100418 @unittest.skipIf(sys.platform == "win32",
419 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100420 def test_dump_traceback_fd(self):
421 with tempfile.TemporaryFile('wb+') as fp:
422 self.check_dump_traceback(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200423
Victor Stinner53386d82012-08-01 19:45:34 +0200424 def test_truncate(self):
425 maxlen = 500
426 func_name = 'x' * (maxlen + 50)
427 truncated = 'x' * maxlen + '...'
428 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200429 import faulthandler
Victor Stinner53386d82012-08-01 19:45:34 +0200430
Victor Stinner6d201682014-08-10 19:50:08 +0200431 def {func_name}():
432 faulthandler.dump_traceback(all_threads=False)
Victor Stinner53386d82012-08-01 19:45:34 +0200433
Victor Stinner6d201682014-08-10 19:50:08 +0200434 {func_name}()
435 """
Victor Stinner53386d82012-08-01 19:45:34 +0200436 code = code.format(
437 func_name=func_name,
438 )
439 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700440 'Stack (most recent call first):',
Victor Stinner53386d82012-08-01 19:45:34 +0200441 ' File "<string>", line 4 in %s' % truncated,
442 ' File "<string>", line 6 in <module>'
443 ]
444 trace, exitcode = self.get_output(code)
445 self.assertEqual(trace, expected)
446 self.assertEqual(exitcode, 0)
447
Victor Stinnerff4cd882011-04-07 11:50:25 +0200448 @unittest.skipIf(not HAVE_THREADS, 'need threads')
Victor Stinner024e37a2011-03-31 01:31:06 +0200449 def check_dump_traceback_threads(self, filename):
450 """
451 Call explicitly dump_traceback(all_threads=True) and check the output.
452 Raise an error if the output doesn't match the expected format.
453 """
454 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200455 import faulthandler
456 from threading import Thread, Event
457 import time
Victor Stinner024e37a2011-03-31 01:31:06 +0200458
Victor Stinner6d201682014-08-10 19:50:08 +0200459 def dump():
460 if {filename}:
461 with open({filename}, "wb") as fp:
462 faulthandler.dump_traceback(fp, all_threads=True)
463 else:
464 faulthandler.dump_traceback(all_threads=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200465
Victor Stinner6d201682014-08-10 19:50:08 +0200466 class Waiter(Thread):
467 # avoid blocking if the main thread raises an exception.
468 daemon = True
Victor Stinner024e37a2011-03-31 01:31:06 +0200469
Victor Stinner6d201682014-08-10 19:50:08 +0200470 def __init__(self):
471 Thread.__init__(self)
472 self.running = Event()
473 self.stop = Event()
Victor Stinner024e37a2011-03-31 01:31:06 +0200474
Victor Stinner6d201682014-08-10 19:50:08 +0200475 def run(self):
476 self.running.set()
477 self.stop.wait()
Victor Stinner024e37a2011-03-31 01:31:06 +0200478
Victor Stinner6d201682014-08-10 19:50:08 +0200479 waiter = Waiter()
480 waiter.start()
481 waiter.running.wait()
482 dump()
483 waiter.stop.set()
484 waiter.join()
485 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200486 code = code.format(filename=repr(filename))
Victor Stinner05585cb2011-03-31 13:29:56 +0200487 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200488 output = '\n'.join(output)
489 if filename:
490 lineno = 8
491 else:
492 lineno = 10
R David Murray44b548d2016-09-08 13:59:53 -0400493 regex = r"""
Victor Stinner6d201682014-08-10 19:50:08 +0200494 ^Thread 0x[0-9a-f]+ \(most recent call first\):
495 (?: File ".*threading.py", line [0-9]+ in [_a-z]+
496 ){{1,3}} File "<string>", line 23 in run
497 File ".*threading.py", line [0-9]+ in _bootstrap_inner
498 File ".*threading.py", line [0-9]+ in _bootstrap
Victor Stinner024e37a2011-03-31 01:31:06 +0200499
Victor Stinner861d9ab2016-03-16 22:45:24 +0100500 Current thread 0x[0-9a-f]+ \(most recent call first\):
Victor Stinner6d201682014-08-10 19:50:08 +0200501 File "<string>", line {lineno} in dump
502 File "<string>", line 28 in <module>$
503 """
504 regex = dedent(regex.format(lineno=lineno)).strip()
Victor Stinner024e37a2011-03-31 01:31:06 +0200505 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200506 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200507
508 def test_dump_traceback_threads(self):
509 self.check_dump_traceback_threads(None)
Victor Stinner19402332011-03-31 18:15:52 +0200510
511 def test_dump_traceback_threads_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200512 with temporary_filename() as filename:
513 self.check_dump_traceback_threads(filename)
514
Victor Stinner95bb7142015-03-12 15:32:03 +0100515 @unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'),
516 'need faulthandler.dump_traceback_later()')
517 def check_dump_traceback_later(self, repeat=False, cancel=False, loops=1,
518 *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200519 """
520 Check how many times the traceback is written in timeout x 2.5 seconds,
521 or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending
522 on repeat and cancel options.
523
524 Raise an error if the output doesn't match the expect format.
525 """
Victor Stinnerc790a532011-04-08 13:39:59 +0200526 timeout_str = str(datetime.timedelta(seconds=TIMEOUT))
Victor Stinner024e37a2011-03-31 01:31:06 +0200527 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200528 import faulthandler
529 import time
Victor Stinner95bb7142015-03-12 15:32:03 +0100530 import sys
531
532 timeout = {timeout}
533 repeat = {repeat}
534 cancel = {cancel}
535 loops = {loops}
536 filename = {filename!r}
537 fd = {fd}
Victor Stinner024e37a2011-03-31 01:31:06 +0200538
Victor Stinner6d201682014-08-10 19:50:08 +0200539 def func(timeout, repeat, cancel, file, loops):
540 for loop in range(loops):
541 faulthandler.dump_traceback_later(timeout, repeat=repeat, file=file)
542 if cancel:
543 faulthandler.cancel_dump_traceback_later()
544 time.sleep(timeout * 5)
545 faulthandler.cancel_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200546
Victor Stinner95bb7142015-03-12 15:32:03 +0100547 if filename:
548 file = open(filename, "wb")
549 elif fd is not None:
550 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200551 else:
552 file = None
553 func(timeout, repeat, cancel, file, loops)
Victor Stinner95bb7142015-03-12 15:32:03 +0100554 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200555 file.close()
556 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200557 code = code.format(
Victor Stinnerde10f402011-04-08 12:57:06 +0200558 timeout=TIMEOUT,
Victor Stinner024e37a2011-03-31 01:31:06 +0200559 repeat=repeat,
560 cancel=cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200561 loops=loops,
Victor Stinner95bb7142015-03-12 15:32:03 +0100562 filename=filename,
563 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200564 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200565 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200566 trace = '\n'.join(trace)
567
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200568 if not cancel:
Victor Stinnerde10f402011-04-08 12:57:06 +0200569 count = loops
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200570 if repeat:
Victor Stinnerde10f402011-04-08 12:57:06 +0200571 count *= 2
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700572 header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+ \(most recent call first\):\n' % timeout_str
Victor Stinner95bb7142015-03-12 15:32:03 +0100573 regex = expected_traceback(17, 26, header, min_count=count)
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200574 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200575 else:
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200576 self.assertEqual(trace, '')
Victor Stinner05585cb2011-03-31 13:29:56 +0200577 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200578
Georg Brandldeb92b52012-09-22 08:58:55 +0200579 def test_dump_traceback_later(self):
580 self.check_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200581
Georg Brandldeb92b52012-09-22 08:58:55 +0200582 def test_dump_traceback_later_repeat(self):
583 self.check_dump_traceback_later(repeat=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200584
Georg Brandldeb92b52012-09-22 08:58:55 +0200585 def test_dump_traceback_later_cancel(self):
586 self.check_dump_traceback_later(cancel=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200587
Georg Brandldeb92b52012-09-22 08:58:55 +0200588 def test_dump_traceback_later_file(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100589 with temporary_filename() as filename:
590 self.check_dump_traceback_later(filename=filename)
591
Victor Stinnerff2a6612015-03-13 11:01:30 +0100592 @unittest.skipIf(sys.platform == "win32",
593 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100594 def test_dump_traceback_later_fd(self):
595 with tempfile.TemporaryFile('wb+') as fp:
596 self.check_dump_traceback_later(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200597
Georg Brandldeb92b52012-09-22 08:58:55 +0200598 def test_dump_traceback_later_twice(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100599 self.check_dump_traceback_later(loops=2)
Victor Stinnerde10f402011-04-08 12:57:06 +0200600
Victor Stinner024e37a2011-03-31 01:31:06 +0200601 @unittest.skipIf(not hasattr(faulthandler, "register"),
602 "need faulthandler.register")
Victor Stinnera01ca122011-04-01 12:56:17 +0200603 def check_register(self, filename=False, all_threads=False,
Victor Stinner95bb7142015-03-12 15:32:03 +0100604 unregister=False, chain=False, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200605 """
606 Register a handler displaying the traceback on a user signal. Raise the
607 signal and check the written traceback.
608
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200609 If chain is True, check that the previous signal handler is called.
610
Victor Stinner024e37a2011-03-31 01:31:06 +0200611 Raise an error if the output doesn't match the expected format.
612 """
Victor Stinnera01ca122011-04-01 12:56:17 +0200613 signum = signal.SIGUSR1
Victor Stinner024e37a2011-03-31 01:31:06 +0200614 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200615 import faulthandler
616 import os
617 import signal
618 import sys
Victor Stinner024e37a2011-03-31 01:31:06 +0200619
Victor Stinner95bb7142015-03-12 15:32:03 +0100620 all_threads = {all_threads}
621 signum = {signum}
622 unregister = {unregister}
623 chain = {chain}
624 filename = {filename!r}
625 fd = {fd}
626
Victor Stinner6d201682014-08-10 19:50:08 +0200627 def func(signum):
628 os.kill(os.getpid(), signum)
Victor Stinner024e37a2011-03-31 01:31:06 +0200629
Victor Stinner6d201682014-08-10 19:50:08 +0200630 def handler(signum, frame):
631 handler.called = True
632 handler.called = False
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200633
Victor Stinner95bb7142015-03-12 15:32:03 +0100634 if filename:
635 file = open(filename, "wb")
636 elif fd is not None:
637 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200638 else:
639 file = None
640 if chain:
641 signal.signal(signum, handler)
642 faulthandler.register(signum, file=file,
Victor Stinner95bb7142015-03-12 15:32:03 +0100643 all_threads=all_threads, chain={chain})
Victor Stinner6d201682014-08-10 19:50:08 +0200644 if unregister:
645 faulthandler.unregister(signum)
646 func(signum)
647 if chain and not handler.called:
648 if file is not None:
649 output = file
650 else:
651 output = sys.stderr
652 print("Error: signal handler not called!", file=output)
653 exitcode = 1
Victor Stinner95bb7142015-03-12 15:32:03 +0100654 else:
655 exitcode = 0
656 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200657 file.close()
658 sys.exit(exitcode)
659 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200660 code = code.format(
Victor Stinner024e37a2011-03-31 01:31:06 +0200661 all_threads=all_threads,
Victor Stinnera01ca122011-04-01 12:56:17 +0200662 signum=signum,
663 unregister=unregister,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200664 chain=chain,
Victor Stinner95bb7142015-03-12 15:32:03 +0100665 filename=filename,
666 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200667 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200668 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200669 trace = '\n'.join(trace)
Victor Stinnera01ca122011-04-01 12:56:17 +0200670 if not unregister:
671 if all_threads:
R David Murray44b548d2016-09-08 13:59:53 -0400672 regex = r'Current thread 0x[0-9a-f]+ \(most recent call first\):\n'
Victor Stinnera01ca122011-04-01 12:56:17 +0200673 else:
R David Murray44b548d2016-09-08 13:59:53 -0400674 regex = r'Stack \(most recent call first\):\n'
Victor Stinner95bb7142015-03-12 15:32:03 +0100675 regex = expected_traceback(14, 32, regex)
Victor Stinnera01ca122011-04-01 12:56:17 +0200676 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200677 else:
Victor Stinnera01ca122011-04-01 12:56:17 +0200678 self.assertEqual(trace, '')
679 if unregister:
680 self.assertNotEqual(exitcode, 0)
681 else:
682 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200683
684 def test_register(self):
685 self.check_register()
686
Victor Stinnera01ca122011-04-01 12:56:17 +0200687 def test_unregister(self):
688 self.check_register(unregister=True)
689
Victor Stinner024e37a2011-03-31 01:31:06 +0200690 def test_register_file(self):
691 with temporary_filename() as filename:
692 self.check_register(filename=filename)
693
Victor Stinnerff2a6612015-03-13 11:01:30 +0100694 @unittest.skipIf(sys.platform == "win32",
695 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100696 def test_register_fd(self):
697 with tempfile.TemporaryFile('wb+') as fp:
698 self.check_register(fd=fp.fileno())
699
Victor Stinner024e37a2011-03-31 01:31:06 +0200700 def test_register_threads(self):
701 self.check_register(all_threads=True)
702
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200703 def test_register_chain(self):
704 self.check_register(chain=True)
705
Victor Stinnere2d66902014-05-14 17:15:50 +0200706 @contextmanager
707 def check_stderr_none(self):
708 stderr = sys.stderr
709 try:
710 sys.stderr = None
711 with self.assertRaises(RuntimeError) as cm:
712 yield
713 self.assertEqual(str(cm.exception), "sys.stderr is None")
714 finally:
715 sys.stderr = stderr
716
717 def test_stderr_None(self):
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +0300718 # Issue #21497: provide a helpful error if sys.stderr is None,
Victor Stinnere2d66902014-05-14 17:15:50 +0200719 # instead of just an attribute error: "None has no attribute fileno".
720 with self.check_stderr_none():
721 faulthandler.enable()
722 with self.check_stderr_none():
723 faulthandler.dump_traceback()
724 if hasattr(faulthandler, 'dump_traceback_later'):
725 with self.check_stderr_none():
726 faulthandler.dump_traceback_later(1e-3)
727 if hasattr(faulthandler, "register"):
728 with self.check_stderr_none():
729 faulthandler.register(signal.SIGUSR1)
730
Victor Stinner404cdc52016-03-23 10:39:17 +0100731 @unittest.skipUnless(MS_WINDOWS, 'specific to Windows')
732 def test_raise_exception(self):
733 for exc, name in (
734 ('EXCEPTION_ACCESS_VIOLATION', 'access violation'),
735 ('EXCEPTION_INT_DIVIDE_BY_ZERO', 'int divide by zero'),
736 ('EXCEPTION_STACK_OVERFLOW', 'stack overflow'),
737 ):
Eric V. Smith06cf6012016-09-03 12:33:38 -0400738 self.check_windows_exception("""
Victor Stinner404cdc52016-03-23 10:39:17 +0100739 import faulthandler
740 faulthandler.enable()
741 faulthandler._raise_exception(faulthandler._{exc})
Eric V. Smith06cf6012016-09-03 12:33:38 -0400742 """.format(exc=exc),
Victor Stinner404cdc52016-03-23 10:39:17 +0100743 3,
744 name)
745
746
Victor Stinner024e37a2011-03-31 01:31:06 +0200747
Victor Stinner024e37a2011-03-31 01:31:06 +0200748if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400749 unittest.main()