blob: bdd8d1a2a6163fd8dfb64e6812327c9677dfad43 [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
Xavier de Gaye6c5f2192016-11-13 20:46:46 +010010from test.support import script_helper, is_android, requires_android_level
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
Xavier de Gaye6c5f2192016-11-13 20:46:46 +010045def requires_raise(test):
46 return (test if not is_android else
47 requires_android_level(24, 'raise() is buggy')(test))
48
Victor Stinner024e37a2011-03-31 01:31:06 +020049class FaultHandlerTests(unittest.TestCase):
Victor Stinner95bb7142015-03-12 15:32:03 +010050 def get_output(self, code, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020051 """
52 Run the specified code in Python (in a new child process) and read the
53 output from the standard error or from a file (if filename is set).
54 Return the output lines as a list.
55
56 Strip the reference count from the standard error for Python debug
57 build, and replace "Current thread 0x00007f8d8fbd9700" by "Current
58 thread XXX".
59 """
Victor Stinner6d201682014-08-10 19:50:08 +020060 code = dedent(code).strip()
Victor Stinner95bb7142015-03-12 15:32:03 +010061 pass_fds = []
62 if fd is not None:
63 pass_fds.append(fd)
Antoine Pitrou77e904e2013-10-08 23:04:32 +020064 with support.SuppressCrashReport():
Victor Stinner95bb7142015-03-12 15:32:03 +010065 process = script_helper.spawn_python('-c', code, pass_fds=pass_fds)
Victor Stinner861d9ab2016-03-16 22:45:24 +010066 with process:
67 stdout, stderr = process.communicate()
68 exitcode = process.wait()
Victor Stinner19402332011-03-31 18:15:52 +020069 output = support.strip_python_stderr(stdout)
70 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020071 if filename:
Victor Stinner19402332011-03-31 18:15:52 +020072 self.assertEqual(output, '')
Victor Stinner024e37a2011-03-31 01:31:06 +020073 with open(filename, "rb") as fp:
74 output = fp.read()
Victor Stinner19402332011-03-31 18:15:52 +020075 output = output.decode('ascii', 'backslashreplace')
Victor Stinner95bb7142015-03-12 15:32:03 +010076 elif fd is not None:
77 self.assertEqual(output, '')
78 os.lseek(fd, os.SEEK_SET, 0)
79 with open(fd, "rb", closefd=False) as fp:
80 output = fp.read()
81 output = output.decode('ascii', 'backslashreplace')
Victor Stinner05585cb2011-03-31 13:29:56 +020082 return output.splitlines(), exitcode
Victor Stinner024e37a2011-03-31 01:31:06 +020083
Victor Stinner404cdc52016-03-23 10:39:17 +010084 def check_error(self, code, line_number, fatal_error, *,
85 filename=None, all_threads=True, other_regex=None,
86 fd=None, know_current_thread=True):
Victor Stinner024e37a2011-03-31 01:31:06 +020087 """
88 Check that the fault handler for fatal errors is enabled and check the
89 traceback from the child process output.
90
91 Raise an error if the output doesn't match the expected format.
92 """
93 if all_threads:
Victor Stinner861d9ab2016-03-16 22:45:24 +010094 if know_current_thread:
95 header = 'Current thread 0x[0-9a-f]+'
96 else:
97 header = 'Thread 0x[0-9a-f]+'
Victor Stinner024e37a2011-03-31 01:31:06 +020098 else:
Victor Stinner861d9ab2016-03-16 22:45:24 +010099 header = 'Stack'
R David Murray44b548d2016-09-08 13:59:53 -0400100 regex = r"""
Victor Stinner404cdc52016-03-23 10:39:17 +0100101 ^{fatal_error}
Victor Stinner024e37a2011-03-31 01:31:06 +0200102
Victor Stinner861d9ab2016-03-16 22:45:24 +0100103 {header} \(most recent call first\):
Victor Stinner6d201682014-08-10 19:50:08 +0200104 File "<string>", line {lineno} in <module>
105 """
106 regex = dedent(regex.format(
Victor Stinner024e37a2011-03-31 01:31:06 +0200107 lineno=line_number,
Victor Stinner404cdc52016-03-23 10:39:17 +0100108 fatal_error=fatal_error,
Victor Stinner861d9ab2016-03-16 22:45:24 +0100109 header=header)).strip()
Victor Stinnerf0480752011-03-31 11:34:08 +0200110 if other_regex:
111 regex += '|' + other_regex
Victor Stinner95bb7142015-03-12 15:32:03 +0100112 output, exitcode = self.get_output(code, filename=filename, fd=fd)
Victor Stinner024e37a2011-03-31 01:31:06 +0200113 output = '\n'.join(output)
114 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200115 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200116
Victor Stinner404cdc52016-03-23 10:39:17 +0100117 def check_fatal_error(self, code, line_number, name_regex, **kw):
118 fatal_error = 'Fatal Python error: %s' % name_regex
119 self.check_error(code, line_number, fatal_error, **kw)
120
121 def check_windows_exception(self, code, line_number, name_regex, **kw):
Victor Stinner412a5e72016-03-23 14:44:14 +0100122 fatal_error = 'Windows fatal exception: %s' % name_regex
Victor Stinner404cdc52016-03-23 10:39:17 +0100123 self.check_error(code, line_number, fatal_error, **kw)
124
Victor Stinner330426c2013-07-03 22:29:42 +0200125 @unittest.skipIf(sys.platform.startswith('aix'),
126 "the first page of memory is a mapped read-only on AIX")
Victor Stinner024e37a2011-03-31 01:31:06 +0200127 def test_read_null(self):
Victor Stinner404cdc52016-03-23 10:39:17 +0100128 if not MS_WINDOWS:
129 self.check_fatal_error("""
130 import faulthandler
131 faulthandler.enable()
132 faulthandler._read_null()
133 """,
134 3,
135 # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion
136 '(?:Segmentation fault'
137 '|Bus error'
138 '|Illegal instruction)')
139 else:
140 self.check_windows_exception("""
141 import faulthandler
142 faulthandler.enable()
143 faulthandler._read_null()
144 """,
145 3,
146 'access violation')
Victor Stinner024e37a2011-03-31 01:31:06 +0200147
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100148 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200149 def test_sigsegv(self):
150 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200151 import faulthandler
152 faulthandler.enable()
153 faulthandler._sigsegv()
154 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200155 3,
156 'Segmentation fault')
157
Victor Stinner861d9ab2016-03-16 22:45:24 +0100158 @unittest.skipIf(not HAVE_THREADS, 'need threads')
159 def test_fatal_error_c_thread(self):
160 self.check_fatal_error("""
161 import faulthandler
162 faulthandler.enable()
163 faulthandler._fatal_error_c_thread()
164 """,
165 3,
166 'in new thread',
167 know_current_thread=False)
168
Victor Stinnerd727e232011-04-01 12:13:55 +0200169 def test_sigabrt(self):
170 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200171 import faulthandler
172 faulthandler.enable()
173 faulthandler._sigabrt()
174 """,
Victor Stinnerd727e232011-04-01 12:13:55 +0200175 3,
176 'Aborted')
177
Victor Stinner024e37a2011-03-31 01:31:06 +0200178 @unittest.skipIf(sys.platform == 'win32',
179 "SIGFPE cannot be caught on Windows")
180 def test_sigfpe(self):
181 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200182 import faulthandler
183 faulthandler.enable()
184 faulthandler._sigfpe()
185 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200186 3,
187 'Floating point exception')
188
Victor Stinner56e8c292014-07-21 12:30:22 +0200189 @unittest.skipIf(_testcapi is None, 'need _testcapi')
190 @unittest.skipUnless(hasattr(signal, 'SIGBUS'), 'need signal.SIGBUS')
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100191 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200192 def test_sigbus(self):
193 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200194 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200195 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200196 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200197
Victor Stinner6d201682014-08-10 19:50:08 +0200198 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200199 _testcapi.raise_signal(signal.SIGBUS)
Victor Stinner6d201682014-08-10 19:50:08 +0200200 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200201 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200202 'Bus error')
203
Victor Stinner56e8c292014-07-21 12:30:22 +0200204 @unittest.skipIf(_testcapi is None, 'need _testcapi')
205 @unittest.skipUnless(hasattr(signal, 'SIGILL'), 'need signal.SIGILL')
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100206 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200207 def test_sigill(self):
208 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200209 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200210 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200211 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200212
Victor Stinner6d201682014-08-10 19:50:08 +0200213 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200214 _testcapi.raise_signal(signal.SIGILL)
Victor Stinner6d201682014-08-10 19:50:08 +0200215 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200216 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200217 'Illegal instruction')
218
219 def test_fatal_error(self):
220 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200221 import faulthandler
222 faulthandler._fatal_error(b'xyz')
223 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200224 2,
225 'xyz')
226
Victor Stinner57003f82016-03-15 17:23:35 +0100227 def test_fatal_error_without_gil(self):
228 self.check_fatal_error("""
229 import faulthandler
230 faulthandler._fatal_error(b'xyz', True)
231 """,
232 2,
233 'xyz')
234
Charles-François Natali3391e642011-09-01 23:08:21 +0200235 @unittest.skipIf(sys.platform.startswith('openbsd') and HAVE_THREADS,
236 "Issue #12868: sigaltstack() doesn't work on "
237 "OpenBSD if Python is compiled with pthread")
Victor Stinner024e37a2011-03-31 01:31:06 +0200238 @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'),
239 'need faulthandler._stack_overflow()')
240 def test_stack_overflow(self):
241 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200242 import faulthandler
243 faulthandler.enable()
244 faulthandler._stack_overflow()
245 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200246 3,
Victor Stinnerf0480752011-03-31 11:34:08 +0200247 '(?:Segmentation fault|Bus error)',
248 other_regex='unable to raise a stack overflow')
Victor Stinner024e37a2011-03-31 01:31:06 +0200249
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100250 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200251 def test_gil_released(self):
252 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200253 import faulthandler
254 faulthandler.enable()
Victor Stinner50838282014-09-30 13:54:14 +0200255 faulthandler._sigsegv(True)
Victor Stinner6d201682014-08-10 19:50:08 +0200256 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200257 3,
Victor Stinner50838282014-09-30 13:54:14 +0200258 'Segmentation fault')
Victor Stinner024e37a2011-03-31 01:31:06 +0200259
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100260 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200261 def test_enable_file(self):
262 with temporary_filename() as filename:
263 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200264 import faulthandler
265 output = open({filename}, 'wb')
266 faulthandler.enable(output)
267 faulthandler._sigsegv()
268 """.format(filename=repr(filename)),
Victor Stinner024e37a2011-03-31 01:31:06 +0200269 4,
Victor Stinner56785392013-06-17 23:37:59 +0200270 'Segmentation fault',
Victor Stinner024e37a2011-03-31 01:31:06 +0200271 filename=filename)
272
Victor Stinnerff2a6612015-03-13 11:01:30 +0100273 @unittest.skipIf(sys.platform == "win32",
274 "subprocess doesn't support pass_fds on Windows")
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100275 @requires_raise
Victor Stinner95bb7142015-03-12 15:32:03 +0100276 def test_enable_fd(self):
277 with tempfile.TemporaryFile('wb+') as fp:
278 fd = fp.fileno()
279 self.check_fatal_error("""
280 import faulthandler
281 import sys
282 faulthandler.enable(%s)
283 faulthandler._sigsegv()
284 """ % fd,
285 4,
286 'Segmentation fault',
287 fd=fd)
288
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100289 @requires_raise
Victor Stinner7bba62f2011-05-07 12:43:00 +0200290 def test_enable_single_thread(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200291 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200292 import faulthandler
293 faulthandler.enable(all_threads=False)
294 faulthandler._sigsegv()
295 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200296 3,
Victor Stinner56785392013-06-17 23:37:59 +0200297 'Segmentation fault',
Victor Stinner7bba62f2011-05-07 12:43:00 +0200298 all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200299
Xavier de Gaye6c5f2192016-11-13 20:46:46 +0100300 @requires_raise
Victor Stinner024e37a2011-03-31 01:31:06 +0200301 def test_disable(self):
302 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200303 import faulthandler
304 faulthandler.enable()
305 faulthandler.disable()
306 faulthandler._sigsegv()
307 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200308 not_expected = 'Fatal Python error'
Antoine Pitrou77e904e2013-10-08 23:04:32 +0200309 stderr, exitcode = self.get_output(code)
Victor Stinner29001c82014-09-25 00:38:48 +0200310 stderr = '\n'.join(stderr)
Victor Stinner024e37a2011-03-31 01:31:06 +0200311 self.assertTrue(not_expected not in stderr,
312 "%r is present in %r" % (not_expected, stderr))
Victor Stinner05585cb2011-03-31 13:29:56 +0200313 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200314
315 def test_is_enabled(self):
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200316 orig_stderr = sys.stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200317 try:
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200318 # regrtest may replace sys.stderr by io.StringIO object, but
319 # faulthandler.enable() requires that sys.stderr has a fileno()
320 # method
Victor Stinner72488502011-06-29 23:24:31 +0200321 sys.stderr = sys.__stderr__
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200322
323 was_enabled = faulthandler.is_enabled()
324 try:
Victor Stinner024e37a2011-03-31 01:31:06 +0200325 faulthandler.enable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200326 self.assertTrue(faulthandler.is_enabled())
Victor Stinner024e37a2011-03-31 01:31:06 +0200327 faulthandler.disable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200328 self.assertFalse(faulthandler.is_enabled())
329 finally:
330 if was_enabled:
331 faulthandler.enable()
332 else:
333 faulthandler.disable()
334 finally:
335 sys.stderr = orig_stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200336
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200337 def test_disabled_by_default(self):
338 # By default, the module should be disabled
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 "-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"False")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200348
349 def test_sys_xoptions(self):
350 # Test python -X faulthandler
351 code = "import faulthandler; print(faulthandler.is_enabled())"
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800352 args = filter(None, (sys.executable,
353 "-E" if sys.flags.ignore_environment else "",
354 "-X", "faulthandler", "-c", code))
355 env = os.environ.copy()
356 env.pop("PYTHONFAULTHANDLER", None)
357 # don't use assert_python_ok() because it always enables faulthandler
358 output = subprocess.check_output(args, env=env)
Victor Stinner88983502013-09-08 11:36:23 +0200359 self.assertEqual(output.rstrip(), b"True")
360
361 def test_env_var(self):
362 # empty env var
363 code = "import faulthandler; print(faulthandler.is_enabled())"
364 args = (sys.executable, "-c", code)
365 env = os.environ.copy()
366 env['PYTHONFAULTHANDLER'] = ''
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800367 # don't use assert_python_ok() because it always enables faulthandler
Victor Stinner88983502013-09-08 11:36:23 +0200368 output = subprocess.check_output(args, env=env)
369 self.assertEqual(output.rstrip(), b"False")
370
371 # non-empty env var
372 env = os.environ.copy()
373 env['PYTHONFAULTHANDLER'] = '1'
374 output = subprocess.check_output(args, env=env)
375 self.assertEqual(output.rstrip(), b"True")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200376
Victor Stinner95bb7142015-03-12 15:32:03 +0100377 def check_dump_traceback(self, *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200378 """
379 Explicitly call dump_traceback() function and check its output.
380 Raise an error if the output doesn't match the expected format.
381 """
382 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200383 import faulthandler
Victor Stinner024e37a2011-03-31 01:31:06 +0200384
Victor Stinner95bb7142015-03-12 15:32:03 +0100385 filename = {filename!r}
386 fd = {fd}
387
Victor Stinner6d201682014-08-10 19:50:08 +0200388 def funcB():
Victor Stinner95bb7142015-03-12 15:32:03 +0100389 if filename:
390 with open(filename, "wb") as fp:
Victor Stinner6d201682014-08-10 19:50:08 +0200391 faulthandler.dump_traceback(fp, all_threads=False)
Victor Stinner95bb7142015-03-12 15:32:03 +0100392 elif fd is not None:
393 faulthandler.dump_traceback(fd,
394 all_threads=False)
Victor Stinner6d201682014-08-10 19:50:08 +0200395 else:
396 faulthandler.dump_traceback(all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200397
Victor Stinner6d201682014-08-10 19:50:08 +0200398 def funcA():
399 funcB()
Victor Stinner024e37a2011-03-31 01:31:06 +0200400
Victor Stinner6d201682014-08-10 19:50:08 +0200401 funcA()
402 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200403 code = code.format(
Victor Stinner95bb7142015-03-12 15:32:03 +0100404 filename=filename,
405 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200406 )
407 if filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100408 lineno = 9
409 elif fd is not None:
410 lineno = 12
Victor Stinner024e37a2011-03-31 01:31:06 +0200411 else:
Victor Stinner95bb7142015-03-12 15:32:03 +0100412 lineno = 14
Victor Stinner024e37a2011-03-31 01:31:06 +0200413 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700414 'Stack (most recent call first):',
Victor Stinner024e37a2011-03-31 01:31:06 +0200415 ' File "<string>", line %s in funcB' % lineno,
Victor Stinner95bb7142015-03-12 15:32:03 +0100416 ' File "<string>", line 17 in funcA',
417 ' File "<string>", line 19 in <module>'
Victor Stinner024e37a2011-03-31 01:31:06 +0200418 ]
Victor Stinner95bb7142015-03-12 15:32:03 +0100419 trace, exitcode = self.get_output(code, filename, fd)
Victor Stinner024e37a2011-03-31 01:31:06 +0200420 self.assertEqual(trace, expected)
Victor Stinner05585cb2011-03-31 13:29:56 +0200421 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200422
423 def test_dump_traceback(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100424 self.check_dump_traceback()
Victor Stinner19402332011-03-31 18:15:52 +0200425
426 def test_dump_traceback_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200427 with temporary_filename() as filename:
Victor Stinner95bb7142015-03-12 15:32:03 +0100428 self.check_dump_traceback(filename=filename)
429
Victor Stinnerff2a6612015-03-13 11:01:30 +0100430 @unittest.skipIf(sys.platform == "win32",
431 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100432 def test_dump_traceback_fd(self):
433 with tempfile.TemporaryFile('wb+') as fp:
434 self.check_dump_traceback(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200435
Victor Stinner53386d82012-08-01 19:45:34 +0200436 def test_truncate(self):
437 maxlen = 500
438 func_name = 'x' * (maxlen + 50)
439 truncated = 'x' * maxlen + '...'
440 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200441 import faulthandler
Victor Stinner53386d82012-08-01 19:45:34 +0200442
Victor Stinner6d201682014-08-10 19:50:08 +0200443 def {func_name}():
444 faulthandler.dump_traceback(all_threads=False)
Victor Stinner53386d82012-08-01 19:45:34 +0200445
Victor Stinner6d201682014-08-10 19:50:08 +0200446 {func_name}()
447 """
Victor Stinner53386d82012-08-01 19:45:34 +0200448 code = code.format(
449 func_name=func_name,
450 )
451 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700452 'Stack (most recent call first):',
Victor Stinner53386d82012-08-01 19:45:34 +0200453 ' File "<string>", line 4 in %s' % truncated,
454 ' File "<string>", line 6 in <module>'
455 ]
456 trace, exitcode = self.get_output(code)
457 self.assertEqual(trace, expected)
458 self.assertEqual(exitcode, 0)
459
Victor Stinnerff4cd882011-04-07 11:50:25 +0200460 @unittest.skipIf(not HAVE_THREADS, 'need threads')
Victor Stinner024e37a2011-03-31 01:31:06 +0200461 def check_dump_traceback_threads(self, filename):
462 """
463 Call explicitly dump_traceback(all_threads=True) and check the output.
464 Raise an error if the output doesn't match the expected format.
465 """
466 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200467 import faulthandler
468 from threading import Thread, Event
469 import time
Victor Stinner024e37a2011-03-31 01:31:06 +0200470
Victor Stinner6d201682014-08-10 19:50:08 +0200471 def dump():
472 if {filename}:
473 with open({filename}, "wb") as fp:
474 faulthandler.dump_traceback(fp, all_threads=True)
475 else:
476 faulthandler.dump_traceback(all_threads=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200477
Victor Stinner6d201682014-08-10 19:50:08 +0200478 class Waiter(Thread):
479 # avoid blocking if the main thread raises an exception.
480 daemon = True
Victor Stinner024e37a2011-03-31 01:31:06 +0200481
Victor Stinner6d201682014-08-10 19:50:08 +0200482 def __init__(self):
483 Thread.__init__(self)
484 self.running = Event()
485 self.stop = Event()
Victor Stinner024e37a2011-03-31 01:31:06 +0200486
Victor Stinner6d201682014-08-10 19:50:08 +0200487 def run(self):
488 self.running.set()
489 self.stop.wait()
Victor Stinner024e37a2011-03-31 01:31:06 +0200490
Victor Stinner6d201682014-08-10 19:50:08 +0200491 waiter = Waiter()
492 waiter.start()
493 waiter.running.wait()
494 dump()
495 waiter.stop.set()
496 waiter.join()
497 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200498 code = code.format(filename=repr(filename))
Victor Stinner05585cb2011-03-31 13:29:56 +0200499 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200500 output = '\n'.join(output)
501 if filename:
502 lineno = 8
503 else:
504 lineno = 10
R David Murray44b548d2016-09-08 13:59:53 -0400505 regex = r"""
Victor Stinner6d201682014-08-10 19:50:08 +0200506 ^Thread 0x[0-9a-f]+ \(most recent call first\):
507 (?: File ".*threading.py", line [0-9]+ in [_a-z]+
508 ){{1,3}} File "<string>", line 23 in run
509 File ".*threading.py", line [0-9]+ in _bootstrap_inner
510 File ".*threading.py", line [0-9]+ in _bootstrap
Victor Stinner024e37a2011-03-31 01:31:06 +0200511
Victor Stinner861d9ab2016-03-16 22:45:24 +0100512 Current thread 0x[0-9a-f]+ \(most recent call first\):
Victor Stinner6d201682014-08-10 19:50:08 +0200513 File "<string>", line {lineno} in dump
514 File "<string>", line 28 in <module>$
515 """
516 regex = dedent(regex.format(lineno=lineno)).strip()
Victor Stinner024e37a2011-03-31 01:31:06 +0200517 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200518 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200519
520 def test_dump_traceback_threads(self):
521 self.check_dump_traceback_threads(None)
Victor Stinner19402332011-03-31 18:15:52 +0200522
523 def test_dump_traceback_threads_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200524 with temporary_filename() as filename:
525 self.check_dump_traceback_threads(filename)
526
Victor Stinner95bb7142015-03-12 15:32:03 +0100527 @unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'),
528 'need faulthandler.dump_traceback_later()')
529 def check_dump_traceback_later(self, repeat=False, cancel=False, loops=1,
530 *, filename=None, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200531 """
532 Check how many times the traceback is written in timeout x 2.5 seconds,
533 or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending
534 on repeat and cancel options.
535
536 Raise an error if the output doesn't match the expect format.
537 """
Victor Stinnerc790a532011-04-08 13:39:59 +0200538 timeout_str = str(datetime.timedelta(seconds=TIMEOUT))
Victor Stinner024e37a2011-03-31 01:31:06 +0200539 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200540 import faulthandler
541 import time
Victor Stinner95bb7142015-03-12 15:32:03 +0100542 import sys
543
544 timeout = {timeout}
545 repeat = {repeat}
546 cancel = {cancel}
547 loops = {loops}
548 filename = {filename!r}
549 fd = {fd}
Victor Stinner024e37a2011-03-31 01:31:06 +0200550
Victor Stinner6d201682014-08-10 19:50:08 +0200551 def func(timeout, repeat, cancel, file, loops):
552 for loop in range(loops):
553 faulthandler.dump_traceback_later(timeout, repeat=repeat, file=file)
554 if cancel:
555 faulthandler.cancel_dump_traceback_later()
556 time.sleep(timeout * 5)
557 faulthandler.cancel_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200558
Victor Stinner95bb7142015-03-12 15:32:03 +0100559 if filename:
560 file = open(filename, "wb")
561 elif fd is not None:
562 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200563 else:
564 file = None
565 func(timeout, repeat, cancel, file, loops)
Victor Stinner95bb7142015-03-12 15:32:03 +0100566 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200567 file.close()
568 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200569 code = code.format(
Victor Stinnerde10f402011-04-08 12:57:06 +0200570 timeout=TIMEOUT,
Victor Stinner024e37a2011-03-31 01:31:06 +0200571 repeat=repeat,
572 cancel=cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200573 loops=loops,
Victor Stinner95bb7142015-03-12 15:32:03 +0100574 filename=filename,
575 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200576 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200577 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200578 trace = '\n'.join(trace)
579
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200580 if not cancel:
Victor Stinnerde10f402011-04-08 12:57:06 +0200581 count = loops
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200582 if repeat:
Victor Stinnerde10f402011-04-08 12:57:06 +0200583 count *= 2
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700584 header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+ \(most recent call first\):\n' % timeout_str
Victor Stinner95bb7142015-03-12 15:32:03 +0100585 regex = expected_traceback(17, 26, header, min_count=count)
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200586 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200587 else:
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200588 self.assertEqual(trace, '')
Victor Stinner05585cb2011-03-31 13:29:56 +0200589 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200590
Georg Brandldeb92b52012-09-22 08:58:55 +0200591 def test_dump_traceback_later(self):
592 self.check_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200593
Georg Brandldeb92b52012-09-22 08:58:55 +0200594 def test_dump_traceback_later_repeat(self):
595 self.check_dump_traceback_later(repeat=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200596
Georg Brandldeb92b52012-09-22 08:58:55 +0200597 def test_dump_traceback_later_cancel(self):
598 self.check_dump_traceback_later(cancel=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200599
Georg Brandldeb92b52012-09-22 08:58:55 +0200600 def test_dump_traceback_later_file(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100601 with temporary_filename() as filename:
602 self.check_dump_traceback_later(filename=filename)
603
Victor Stinnerff2a6612015-03-13 11:01:30 +0100604 @unittest.skipIf(sys.platform == "win32",
605 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100606 def test_dump_traceback_later_fd(self):
607 with tempfile.TemporaryFile('wb+') as fp:
608 self.check_dump_traceback_later(fd=fp.fileno())
Victor Stinner024e37a2011-03-31 01:31:06 +0200609
Georg Brandldeb92b52012-09-22 08:58:55 +0200610 def test_dump_traceback_later_twice(self):
Victor Stinner95bb7142015-03-12 15:32:03 +0100611 self.check_dump_traceback_later(loops=2)
Victor Stinnerde10f402011-04-08 12:57:06 +0200612
Victor Stinner024e37a2011-03-31 01:31:06 +0200613 @unittest.skipIf(not hasattr(faulthandler, "register"),
614 "need faulthandler.register")
Victor Stinnera01ca122011-04-01 12:56:17 +0200615 def check_register(self, filename=False, all_threads=False,
Victor Stinner95bb7142015-03-12 15:32:03 +0100616 unregister=False, chain=False, fd=None):
Victor Stinner024e37a2011-03-31 01:31:06 +0200617 """
618 Register a handler displaying the traceback on a user signal. Raise the
619 signal and check the written traceback.
620
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200621 If chain is True, check that the previous signal handler is called.
622
Victor Stinner024e37a2011-03-31 01:31:06 +0200623 Raise an error if the output doesn't match the expected format.
624 """
Victor Stinnera01ca122011-04-01 12:56:17 +0200625 signum = signal.SIGUSR1
Victor Stinner024e37a2011-03-31 01:31:06 +0200626 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200627 import faulthandler
628 import os
629 import signal
630 import sys
Victor Stinner024e37a2011-03-31 01:31:06 +0200631
Victor Stinner95bb7142015-03-12 15:32:03 +0100632 all_threads = {all_threads}
633 signum = {signum}
634 unregister = {unregister}
635 chain = {chain}
636 filename = {filename!r}
637 fd = {fd}
638
Victor Stinner6d201682014-08-10 19:50:08 +0200639 def func(signum):
640 os.kill(os.getpid(), signum)
Victor Stinner024e37a2011-03-31 01:31:06 +0200641
Victor Stinner6d201682014-08-10 19:50:08 +0200642 def handler(signum, frame):
643 handler.called = True
644 handler.called = False
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200645
Victor Stinner95bb7142015-03-12 15:32:03 +0100646 if filename:
647 file = open(filename, "wb")
648 elif fd is not None:
649 file = sys.stderr.fileno()
Victor Stinner6d201682014-08-10 19:50:08 +0200650 else:
651 file = None
652 if chain:
653 signal.signal(signum, handler)
654 faulthandler.register(signum, file=file,
Victor Stinner95bb7142015-03-12 15:32:03 +0100655 all_threads=all_threads, chain={chain})
Victor Stinner6d201682014-08-10 19:50:08 +0200656 if unregister:
657 faulthandler.unregister(signum)
658 func(signum)
659 if chain and not handler.called:
660 if file is not None:
661 output = file
662 else:
663 output = sys.stderr
664 print("Error: signal handler not called!", file=output)
665 exitcode = 1
Victor Stinner95bb7142015-03-12 15:32:03 +0100666 else:
667 exitcode = 0
668 if filename:
Victor Stinner6d201682014-08-10 19:50:08 +0200669 file.close()
670 sys.exit(exitcode)
671 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200672 code = code.format(
Victor Stinner024e37a2011-03-31 01:31:06 +0200673 all_threads=all_threads,
Victor Stinnera01ca122011-04-01 12:56:17 +0200674 signum=signum,
675 unregister=unregister,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200676 chain=chain,
Victor Stinner95bb7142015-03-12 15:32:03 +0100677 filename=filename,
678 fd=fd,
Victor Stinner024e37a2011-03-31 01:31:06 +0200679 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200680 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200681 trace = '\n'.join(trace)
Victor Stinnera01ca122011-04-01 12:56:17 +0200682 if not unregister:
683 if all_threads:
R David Murray44b548d2016-09-08 13:59:53 -0400684 regex = r'Current thread 0x[0-9a-f]+ \(most recent call first\):\n'
Victor Stinnera01ca122011-04-01 12:56:17 +0200685 else:
R David Murray44b548d2016-09-08 13:59:53 -0400686 regex = r'Stack \(most recent call first\):\n'
Victor Stinner95bb7142015-03-12 15:32:03 +0100687 regex = expected_traceback(14, 32, regex)
Victor Stinnera01ca122011-04-01 12:56:17 +0200688 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200689 else:
Victor Stinnera01ca122011-04-01 12:56:17 +0200690 self.assertEqual(trace, '')
691 if unregister:
692 self.assertNotEqual(exitcode, 0)
693 else:
694 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200695
696 def test_register(self):
697 self.check_register()
698
Victor Stinnera01ca122011-04-01 12:56:17 +0200699 def test_unregister(self):
700 self.check_register(unregister=True)
701
Victor Stinner024e37a2011-03-31 01:31:06 +0200702 def test_register_file(self):
703 with temporary_filename() as filename:
704 self.check_register(filename=filename)
705
Victor Stinnerff2a6612015-03-13 11:01:30 +0100706 @unittest.skipIf(sys.platform == "win32",
707 "subprocess doesn't support pass_fds on Windows")
Victor Stinner95bb7142015-03-12 15:32:03 +0100708 def test_register_fd(self):
709 with tempfile.TemporaryFile('wb+') as fp:
710 self.check_register(fd=fp.fileno())
711
Victor Stinner024e37a2011-03-31 01:31:06 +0200712 def test_register_threads(self):
713 self.check_register(all_threads=True)
714
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200715 def test_register_chain(self):
716 self.check_register(chain=True)
717
Victor Stinnere2d66902014-05-14 17:15:50 +0200718 @contextmanager
719 def check_stderr_none(self):
720 stderr = sys.stderr
721 try:
722 sys.stderr = None
723 with self.assertRaises(RuntimeError) as cm:
724 yield
725 self.assertEqual(str(cm.exception), "sys.stderr is None")
726 finally:
727 sys.stderr = stderr
728
729 def test_stderr_None(self):
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +0300730 # Issue #21497: provide a helpful error if sys.stderr is None,
Victor Stinnere2d66902014-05-14 17:15:50 +0200731 # instead of just an attribute error: "None has no attribute fileno".
732 with self.check_stderr_none():
733 faulthandler.enable()
734 with self.check_stderr_none():
735 faulthandler.dump_traceback()
736 if hasattr(faulthandler, 'dump_traceback_later'):
737 with self.check_stderr_none():
738 faulthandler.dump_traceback_later(1e-3)
739 if hasattr(faulthandler, "register"):
740 with self.check_stderr_none():
741 faulthandler.register(signal.SIGUSR1)
742
Victor Stinner404cdc52016-03-23 10:39:17 +0100743 @unittest.skipUnless(MS_WINDOWS, 'specific to Windows')
744 def test_raise_exception(self):
745 for exc, name in (
746 ('EXCEPTION_ACCESS_VIOLATION', 'access violation'),
747 ('EXCEPTION_INT_DIVIDE_BY_ZERO', 'int divide by zero'),
748 ('EXCEPTION_STACK_OVERFLOW', 'stack overflow'),
749 ):
Eric V. Smith451d0e32016-09-09 21:56:20 -0400750 self.check_windows_exception(f"""
Victor Stinner404cdc52016-03-23 10:39:17 +0100751 import faulthandler
752 faulthandler.enable()
753 faulthandler._raise_exception(faulthandler._{exc})
Eric V. Smith451d0e32016-09-09 21:56:20 -0400754 """,
Victor Stinner404cdc52016-03-23 10:39:17 +0100755 3,
756 name)
757
758
Victor Stinner024e37a2011-03-31 01:31:06 +0200759
Victor Stinner024e37a2011-03-31 01:31:06 +0200760if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400761 unittest.main()