blob: e3960541f527c5d5cdaf005b8cb2524ea08f4cc0 [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 Stinner05585cb2011-03-31 13:29:56 +020045 def get_output(self, code, filename=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()
Antoine Pitrou77e904e2013-10-08 23:04:32 +020056 with support.SuppressCrashReport():
57 process = script_helper.spawn_python('-c', code)
Victor Stinner024e37a2011-03-31 01:31:06 +020058 stdout, stderr = process.communicate()
59 exitcode = process.wait()
Victor Stinner19402332011-03-31 18:15:52 +020060 output = support.strip_python_stderr(stdout)
61 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020062 if filename:
Victor Stinner19402332011-03-31 18:15:52 +020063 self.assertEqual(output, '')
Victor Stinner024e37a2011-03-31 01:31:06 +020064 with open(filename, "rb") as fp:
65 output = fp.read()
Victor Stinner19402332011-03-31 18:15:52 +020066 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020067 output = re.sub('Current thread 0x[0-9a-f]+',
68 'Current thread XXX',
69 output)
Victor Stinner05585cb2011-03-31 13:29:56 +020070 return output.splitlines(), exitcode
Victor Stinner024e37a2011-03-31 01:31:06 +020071
72 def check_fatal_error(self, code, line_number, name_regex,
Victor Stinner7bba62f2011-05-07 12:43:00 +020073 filename=None, all_threads=True, other_regex=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020074 """
75 Check that the fault handler for fatal errors is enabled and check the
76 traceback from the child process output.
77
78 Raise an error if the output doesn't match the expected format.
79 """
80 if all_threads:
Guido van Rossum7be5d7d2013-10-20 18:21:02 -070081 header = 'Current thread XXX (most recent call first)'
Victor Stinner024e37a2011-03-31 01:31:06 +020082 else:
Guido van Rossum7be5d7d2013-10-20 18:21:02 -070083 header = 'Stack (most recent call first)'
Victor Stinner024e37a2011-03-31 01:31:06 +020084 regex = """
Victor Stinner6d201682014-08-10 19:50:08 +020085 ^Fatal Python error: {name}
Victor Stinner024e37a2011-03-31 01:31:06 +020086
Victor Stinner6d201682014-08-10 19:50:08 +020087 {header}:
88 File "<string>", line {lineno} in <module>
89 """
90 regex = dedent(regex.format(
Victor Stinner024e37a2011-03-31 01:31:06 +020091 lineno=line_number,
92 name=name_regex,
Victor Stinner6d201682014-08-10 19:50:08 +020093 header=re.escape(header))).strip()
Victor Stinnerf0480752011-03-31 11:34:08 +020094 if other_regex:
95 regex += '|' + other_regex
Antoine Pitrou77e904e2013-10-08 23:04:32 +020096 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +020097 output = '\n'.join(output)
98 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +020099 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200100
Victor Stinner330426c2013-07-03 22:29:42 +0200101 @unittest.skipIf(sys.platform.startswith('aix'),
102 "the first page of memory is a mapped read-only on AIX")
Victor Stinner024e37a2011-03-31 01:31:06 +0200103 def test_read_null(self):
104 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200105 import faulthandler
106 faulthandler.enable()
107 faulthandler._read_null()
108 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200109 3,
Victor Stinnera8db3782011-08-08 22:43:45 +0200110 # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion
111 '(?:Segmentation fault|Bus error|Illegal instruction)')
Victor Stinner024e37a2011-03-31 01:31:06 +0200112
113 def test_sigsegv(self):
114 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200115 import faulthandler
116 faulthandler.enable()
117 faulthandler._sigsegv()
118 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200119 3,
120 'Segmentation fault')
121
Victor Stinnerd727e232011-04-01 12:13:55 +0200122 def test_sigabrt(self):
123 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200124 import faulthandler
125 faulthandler.enable()
126 faulthandler._sigabrt()
127 """,
Victor Stinnerd727e232011-04-01 12:13:55 +0200128 3,
129 'Aborted')
130
Victor Stinner024e37a2011-03-31 01:31:06 +0200131 @unittest.skipIf(sys.platform == 'win32',
132 "SIGFPE cannot be caught on Windows")
133 def test_sigfpe(self):
134 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200135 import faulthandler
136 faulthandler.enable()
137 faulthandler._sigfpe()
138 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200139 3,
140 'Floating point exception')
141
Victor Stinner56e8c292014-07-21 12:30:22 +0200142 @unittest.skipIf(_testcapi is None, 'need _testcapi')
143 @unittest.skipUnless(hasattr(signal, 'SIGBUS'), 'need signal.SIGBUS')
Victor Stinner024e37a2011-03-31 01:31:06 +0200144 def test_sigbus(self):
145 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200146 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200147 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200148 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200149
Victor Stinner6d201682014-08-10 19:50:08 +0200150 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200151 _testcapi.raise_signal(signal.SIGBUS)
Victor Stinner6d201682014-08-10 19:50:08 +0200152 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200153 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200154 'Bus error')
155
Victor Stinner56e8c292014-07-21 12:30:22 +0200156 @unittest.skipIf(_testcapi is None, 'need _testcapi')
157 @unittest.skipUnless(hasattr(signal, 'SIGILL'), 'need signal.SIGILL')
Victor Stinner024e37a2011-03-31 01:31:06 +0200158 def test_sigill(self):
159 self.check_fatal_error("""
Victor Stinner68e08082014-08-10 19:51:05 +0200160 import _testcapi
Victor Stinner6d201682014-08-10 19:50:08 +0200161 import faulthandler
Victor Stinner68e08082014-08-10 19:51:05 +0200162 import signal
Victor Stinner56e8c292014-07-21 12:30:22 +0200163
Victor Stinner6d201682014-08-10 19:50:08 +0200164 faulthandler.enable()
Victor Stinner68e08082014-08-10 19:51:05 +0200165 _testcapi.raise_signal(signal.SIGILL)
Victor Stinner6d201682014-08-10 19:50:08 +0200166 """,
Victor Stinner56e8c292014-07-21 12:30:22 +0200167 6,
Victor Stinner024e37a2011-03-31 01:31:06 +0200168 'Illegal instruction')
169
170 def test_fatal_error(self):
171 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200172 import faulthandler
173 faulthandler._fatal_error(b'xyz')
174 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200175 2,
176 'xyz')
177
Charles-François Natali3391e642011-09-01 23:08:21 +0200178 @unittest.skipIf(sys.platform.startswith('openbsd') and HAVE_THREADS,
179 "Issue #12868: sigaltstack() doesn't work on "
180 "OpenBSD if Python is compiled with pthread")
Victor Stinner024e37a2011-03-31 01:31:06 +0200181 @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'),
182 'need faulthandler._stack_overflow()')
183 def test_stack_overflow(self):
184 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200185 import faulthandler
186 faulthandler.enable()
187 faulthandler._stack_overflow()
188 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200189 3,
Victor Stinnerf0480752011-03-31 11:34:08 +0200190 '(?:Segmentation fault|Bus error)',
191 other_regex='unable to raise a stack overflow')
Victor Stinner024e37a2011-03-31 01:31:06 +0200192
193 def test_gil_released(self):
194 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200195 import faulthandler
196 faulthandler.enable()
Victor Stinner50838282014-09-30 13:54:14 +0200197 faulthandler._sigsegv(True)
Victor Stinner6d201682014-08-10 19:50:08 +0200198 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200199 3,
Victor Stinner50838282014-09-30 13:54:14 +0200200 'Segmentation fault')
Victor Stinner024e37a2011-03-31 01:31:06 +0200201
202 def test_enable_file(self):
203 with temporary_filename() as filename:
204 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200205 import faulthandler
206 output = open({filename}, 'wb')
207 faulthandler.enable(output)
208 faulthandler._sigsegv()
209 """.format(filename=repr(filename)),
Victor Stinner024e37a2011-03-31 01:31:06 +0200210 4,
Victor Stinner56785392013-06-17 23:37:59 +0200211 'Segmentation fault',
Victor Stinner024e37a2011-03-31 01:31:06 +0200212 filename=filename)
213
Victor Stinner7bba62f2011-05-07 12:43:00 +0200214 def test_enable_single_thread(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200215 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200216 import faulthandler
217 faulthandler.enable(all_threads=False)
218 faulthandler._sigsegv()
219 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200220 3,
Victor Stinner56785392013-06-17 23:37:59 +0200221 'Segmentation fault',
Victor Stinner7bba62f2011-05-07 12:43:00 +0200222 all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200223
224 def test_disable(self):
225 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200226 import faulthandler
227 faulthandler.enable()
228 faulthandler.disable()
229 faulthandler._sigsegv()
230 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200231 not_expected = 'Fatal Python error'
Antoine Pitrou77e904e2013-10-08 23:04:32 +0200232 stderr, exitcode = self.get_output(code)
Victor Stinner29001c82014-09-25 00:38:48 +0200233 stderr = '\n'.join(stderr)
Victor Stinner024e37a2011-03-31 01:31:06 +0200234 self.assertTrue(not_expected not in stderr,
235 "%r is present in %r" % (not_expected, stderr))
Victor Stinner05585cb2011-03-31 13:29:56 +0200236 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200237
238 def test_is_enabled(self):
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200239 orig_stderr = sys.stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200240 try:
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200241 # regrtest may replace sys.stderr by io.StringIO object, but
242 # faulthandler.enable() requires that sys.stderr has a fileno()
243 # method
Victor Stinner72488502011-06-29 23:24:31 +0200244 sys.stderr = sys.__stderr__
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200245
246 was_enabled = faulthandler.is_enabled()
247 try:
Victor Stinner024e37a2011-03-31 01:31:06 +0200248 faulthandler.enable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200249 self.assertTrue(faulthandler.is_enabled())
Victor Stinner024e37a2011-03-31 01:31:06 +0200250 faulthandler.disable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200251 self.assertFalse(faulthandler.is_enabled())
252 finally:
253 if was_enabled:
254 faulthandler.enable()
255 else:
256 faulthandler.disable()
257 finally:
258 sys.stderr = orig_stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200259
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200260 def test_disabled_by_default(self):
261 # By default, the module should be disabled
262 code = "import faulthandler; print(faulthandler.is_enabled())"
Victor Stinneref8115e2013-06-25 21:54:17 +0200263 args = (sys.executable, '-E', '-c', code)
Victor Stinner88983502013-09-08 11:36:23 +0200264 # don't use assert_python_ok() because it always enable faulthandler
265 output = subprocess.check_output(args)
266 self.assertEqual(output.rstrip(), b"False")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200267
268 def test_sys_xoptions(self):
269 # Test python -X faulthandler
270 code = "import faulthandler; print(faulthandler.is_enabled())"
Victor Stinner88983502013-09-08 11:36:23 +0200271 args = (sys.executable, "-E", "-X", "faulthandler", "-c", code)
272 # don't use assert_python_ok() because it always enable faulthandler
273 output = subprocess.check_output(args)
274 self.assertEqual(output.rstrip(), b"True")
275
276 def test_env_var(self):
277 # empty env var
278 code = "import faulthandler; print(faulthandler.is_enabled())"
279 args = (sys.executable, "-c", code)
280 env = os.environ.copy()
281 env['PYTHONFAULTHANDLER'] = ''
282 # don't use assert_python_ok() because it always enable faulthandler
283 output = subprocess.check_output(args, env=env)
284 self.assertEqual(output.rstrip(), b"False")
285
286 # non-empty env var
287 env = os.environ.copy()
288 env['PYTHONFAULTHANDLER'] = '1'
289 output = subprocess.check_output(args, env=env)
290 self.assertEqual(output.rstrip(), b"True")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200291
Victor Stinner024e37a2011-03-31 01:31:06 +0200292 def check_dump_traceback(self, filename):
293 """
294 Explicitly call dump_traceback() function and check its output.
295 Raise an error if the output doesn't match the expected format.
296 """
297 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200298 import faulthandler
Victor Stinner024e37a2011-03-31 01:31:06 +0200299
Victor Stinner6d201682014-08-10 19:50:08 +0200300 def funcB():
301 if {has_filename}:
302 with open({filename}, "wb") as fp:
303 faulthandler.dump_traceback(fp, all_threads=False)
304 else:
305 faulthandler.dump_traceback(all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200306
Victor Stinner6d201682014-08-10 19:50:08 +0200307 def funcA():
308 funcB()
Victor Stinner024e37a2011-03-31 01:31:06 +0200309
Victor Stinner6d201682014-08-10 19:50:08 +0200310 funcA()
311 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200312 code = code.format(
313 filename=repr(filename),
314 has_filename=bool(filename),
315 )
316 if filename:
317 lineno = 6
318 else:
319 lineno = 8
320 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700321 'Stack (most recent call first):',
Victor Stinner024e37a2011-03-31 01:31:06 +0200322 ' File "<string>", line %s in funcB' % lineno,
323 ' File "<string>", line 11 in funcA',
324 ' File "<string>", line 13 in <module>'
325 ]
Victor Stinner05585cb2011-03-31 13:29:56 +0200326 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200327 self.assertEqual(trace, expected)
Victor Stinner05585cb2011-03-31 13:29:56 +0200328 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200329
330 def test_dump_traceback(self):
331 self.check_dump_traceback(None)
Victor Stinner19402332011-03-31 18:15:52 +0200332
333 def test_dump_traceback_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200334 with temporary_filename() as filename:
335 self.check_dump_traceback(filename)
336
Victor Stinner53386d82012-08-01 19:45:34 +0200337 def test_truncate(self):
338 maxlen = 500
339 func_name = 'x' * (maxlen + 50)
340 truncated = 'x' * maxlen + '...'
341 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200342 import faulthandler
Victor Stinner53386d82012-08-01 19:45:34 +0200343
Victor Stinner6d201682014-08-10 19:50:08 +0200344 def {func_name}():
345 faulthandler.dump_traceback(all_threads=False)
Victor Stinner53386d82012-08-01 19:45:34 +0200346
Victor Stinner6d201682014-08-10 19:50:08 +0200347 {func_name}()
348 """
Victor Stinner53386d82012-08-01 19:45:34 +0200349 code = code.format(
350 func_name=func_name,
351 )
352 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700353 'Stack (most recent call first):',
Victor Stinner53386d82012-08-01 19:45:34 +0200354 ' File "<string>", line 4 in %s' % truncated,
355 ' File "<string>", line 6 in <module>'
356 ]
357 trace, exitcode = self.get_output(code)
358 self.assertEqual(trace, expected)
359 self.assertEqual(exitcode, 0)
360
Victor Stinnerff4cd882011-04-07 11:50:25 +0200361 @unittest.skipIf(not HAVE_THREADS, 'need threads')
Victor Stinner024e37a2011-03-31 01:31:06 +0200362 def check_dump_traceback_threads(self, filename):
363 """
364 Call explicitly dump_traceback(all_threads=True) and check the output.
365 Raise an error if the output doesn't match the expected format.
366 """
367 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200368 import faulthandler
369 from threading import Thread, Event
370 import time
Victor Stinner024e37a2011-03-31 01:31:06 +0200371
Victor Stinner6d201682014-08-10 19:50:08 +0200372 def dump():
373 if {filename}:
374 with open({filename}, "wb") as fp:
375 faulthandler.dump_traceback(fp, all_threads=True)
376 else:
377 faulthandler.dump_traceback(all_threads=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200378
Victor Stinner6d201682014-08-10 19:50:08 +0200379 class Waiter(Thread):
380 # avoid blocking if the main thread raises an exception.
381 daemon = True
Victor Stinner024e37a2011-03-31 01:31:06 +0200382
Victor Stinner6d201682014-08-10 19:50:08 +0200383 def __init__(self):
384 Thread.__init__(self)
385 self.running = Event()
386 self.stop = Event()
Victor Stinner024e37a2011-03-31 01:31:06 +0200387
Victor Stinner6d201682014-08-10 19:50:08 +0200388 def run(self):
389 self.running.set()
390 self.stop.wait()
Victor Stinner024e37a2011-03-31 01:31:06 +0200391
Victor Stinner6d201682014-08-10 19:50:08 +0200392 waiter = Waiter()
393 waiter.start()
394 waiter.running.wait()
395 dump()
396 waiter.stop.set()
397 waiter.join()
398 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200399 code = code.format(filename=repr(filename))
Victor Stinner05585cb2011-03-31 13:29:56 +0200400 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200401 output = '\n'.join(output)
402 if filename:
403 lineno = 8
404 else:
405 lineno = 10
406 regex = """
Victor Stinner6d201682014-08-10 19:50:08 +0200407 ^Thread 0x[0-9a-f]+ \(most recent call first\):
408 (?: File ".*threading.py", line [0-9]+ in [_a-z]+
409 ){{1,3}} File "<string>", line 23 in run
410 File ".*threading.py", line [0-9]+ in _bootstrap_inner
411 File ".*threading.py", line [0-9]+ in _bootstrap
Victor Stinner024e37a2011-03-31 01:31:06 +0200412
Victor Stinner6d201682014-08-10 19:50:08 +0200413 Current thread XXX \(most recent call first\):
414 File "<string>", line {lineno} in dump
415 File "<string>", line 28 in <module>$
416 """
417 regex = dedent(regex.format(lineno=lineno)).strip()
Victor Stinner024e37a2011-03-31 01:31:06 +0200418 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200419 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200420
421 def test_dump_traceback_threads(self):
422 self.check_dump_traceback_threads(None)
Victor Stinner19402332011-03-31 18:15:52 +0200423
424 def test_dump_traceback_threads_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200425 with temporary_filename() as filename:
426 self.check_dump_traceback_threads(filename)
427
Georg Brandldeb92b52012-09-22 08:58:55 +0200428 def _check_dump_traceback_later(self, repeat, cancel, filename, loops):
Victor Stinner024e37a2011-03-31 01:31:06 +0200429 """
430 Check how many times the traceback is written in timeout x 2.5 seconds,
431 or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending
432 on repeat and cancel options.
433
434 Raise an error if the output doesn't match the expect format.
435 """
Victor Stinnerc790a532011-04-08 13:39:59 +0200436 timeout_str = str(datetime.timedelta(seconds=TIMEOUT))
Victor Stinner024e37a2011-03-31 01:31:06 +0200437 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200438 import faulthandler
439 import time
Victor Stinner024e37a2011-03-31 01:31:06 +0200440
Victor Stinner6d201682014-08-10 19:50:08 +0200441 def func(timeout, repeat, cancel, file, loops):
442 for loop in range(loops):
443 faulthandler.dump_traceback_later(timeout, repeat=repeat, file=file)
444 if cancel:
445 faulthandler.cancel_dump_traceback_later()
446 time.sleep(timeout * 5)
447 faulthandler.cancel_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200448
Victor Stinner6d201682014-08-10 19:50:08 +0200449 timeout = {timeout}
450 repeat = {repeat}
451 cancel = {cancel}
452 loops = {loops}
453 if {has_filename}:
454 file = open({filename}, "wb")
455 else:
456 file = None
457 func(timeout, repeat, cancel, file, loops)
458 if file is not None:
459 file.close()
460 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200461 code = code.format(
Victor Stinnerde10f402011-04-08 12:57:06 +0200462 timeout=TIMEOUT,
Victor Stinner024e37a2011-03-31 01:31:06 +0200463 repeat=repeat,
464 cancel=cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200465 loops=loops,
466 has_filename=bool(filename),
467 filename=repr(filename),
Victor Stinner024e37a2011-03-31 01:31:06 +0200468 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200469 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200470 trace = '\n'.join(trace)
471
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200472 if not cancel:
Victor Stinnerde10f402011-04-08 12:57:06 +0200473 count = loops
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200474 if repeat:
Victor Stinnerde10f402011-04-08 12:57:06 +0200475 count *= 2
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700476 header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+ \(most recent call first\):\n' % timeout_str
Victor Stinner301f3f02011-06-01 13:49:12 +0200477 regex = expected_traceback(9, 20, header, min_count=count)
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200478 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200479 else:
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200480 self.assertEqual(trace, '')
Victor Stinner05585cb2011-03-31 13:29:56 +0200481 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200482
Georg Brandldeb92b52012-09-22 08:58:55 +0200483 @unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'),
484 'need faulthandler.dump_traceback_later()')
485 def check_dump_traceback_later(self, repeat=False, cancel=False,
Victor Stinnerde10f402011-04-08 12:57:06 +0200486 file=False, twice=False):
487 if twice:
488 loops = 2
489 else:
490 loops = 1
Victor Stinner024e37a2011-03-31 01:31:06 +0200491 if file:
492 with temporary_filename() as filename:
Georg Brandldeb92b52012-09-22 08:58:55 +0200493 self._check_dump_traceback_later(repeat, cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200494 filename, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200495 else:
Georg Brandldeb92b52012-09-22 08:58:55 +0200496 self._check_dump_traceback_later(repeat, cancel, None, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200497
Georg Brandldeb92b52012-09-22 08:58:55 +0200498 def test_dump_traceback_later(self):
499 self.check_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200500
Georg Brandldeb92b52012-09-22 08:58:55 +0200501 def test_dump_traceback_later_repeat(self):
502 self.check_dump_traceback_later(repeat=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200503
Georg Brandldeb92b52012-09-22 08:58:55 +0200504 def test_dump_traceback_later_cancel(self):
505 self.check_dump_traceback_later(cancel=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200506
Georg Brandldeb92b52012-09-22 08:58:55 +0200507 def test_dump_traceback_later_file(self):
508 self.check_dump_traceback_later(file=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200509
Georg Brandldeb92b52012-09-22 08:58:55 +0200510 def test_dump_traceback_later_twice(self):
511 self.check_dump_traceback_later(twice=True)
Victor Stinnerde10f402011-04-08 12:57:06 +0200512
Victor Stinner024e37a2011-03-31 01:31:06 +0200513 @unittest.skipIf(not hasattr(faulthandler, "register"),
514 "need faulthandler.register")
Victor Stinnera01ca122011-04-01 12:56:17 +0200515 def check_register(self, filename=False, all_threads=False,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200516 unregister=False, chain=False):
Victor Stinner024e37a2011-03-31 01:31:06 +0200517 """
518 Register a handler displaying the traceback on a user signal. Raise the
519 signal and check the written traceback.
520
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200521 If chain is True, check that the previous signal handler is called.
522
Victor Stinner024e37a2011-03-31 01:31:06 +0200523 Raise an error if the output doesn't match the expected format.
524 """
Victor Stinnera01ca122011-04-01 12:56:17 +0200525 signum = signal.SIGUSR1
Victor Stinner024e37a2011-03-31 01:31:06 +0200526 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200527 import faulthandler
528 import os
529 import signal
530 import sys
Victor Stinner024e37a2011-03-31 01:31:06 +0200531
Victor Stinner6d201682014-08-10 19:50:08 +0200532 def func(signum):
533 os.kill(os.getpid(), signum)
Victor Stinner024e37a2011-03-31 01:31:06 +0200534
Victor Stinner6d201682014-08-10 19:50:08 +0200535 def handler(signum, frame):
536 handler.called = True
537 handler.called = False
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200538
Victor Stinner6d201682014-08-10 19:50:08 +0200539 exitcode = 0
540 signum = {signum}
541 unregister = {unregister}
542 chain = {chain}
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200543
Victor Stinner6d201682014-08-10 19:50:08 +0200544 if {has_filename}:
545 file = open({filename}, "wb")
546 else:
547 file = None
548 if chain:
549 signal.signal(signum, handler)
550 faulthandler.register(signum, file=file,
551 all_threads={all_threads}, chain={chain})
552 if unregister:
553 faulthandler.unregister(signum)
554 func(signum)
555 if chain and not handler.called:
556 if file is not None:
557 output = file
558 else:
559 output = sys.stderr
560 print("Error: signal handler not called!", file=output)
561 exitcode = 1
562 if file is not None:
563 file.close()
564 sys.exit(exitcode)
565 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200566 code = code.format(
567 filename=repr(filename),
568 has_filename=bool(filename),
569 all_threads=all_threads,
Victor Stinnera01ca122011-04-01 12:56:17 +0200570 signum=signum,
571 unregister=unregister,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200572 chain=chain,
Victor Stinner024e37a2011-03-31 01:31:06 +0200573 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200574 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200575 trace = '\n'.join(trace)
Victor Stinnera01ca122011-04-01 12:56:17 +0200576 if not unregister:
577 if all_threads:
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700578 regex = 'Current thread XXX \(most recent call first\):\n'
Victor Stinnera01ca122011-04-01 12:56:17 +0200579 else:
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700580 regex = 'Stack \(most recent call first\):\n'
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200581 regex = expected_traceback(7, 28, regex)
Victor Stinnera01ca122011-04-01 12:56:17 +0200582 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200583 else:
Victor Stinnera01ca122011-04-01 12:56:17 +0200584 self.assertEqual(trace, '')
585 if unregister:
586 self.assertNotEqual(exitcode, 0)
587 else:
588 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200589
590 def test_register(self):
591 self.check_register()
592
Victor Stinnera01ca122011-04-01 12:56:17 +0200593 def test_unregister(self):
594 self.check_register(unregister=True)
595
Victor Stinner024e37a2011-03-31 01:31:06 +0200596 def test_register_file(self):
597 with temporary_filename() as filename:
598 self.check_register(filename=filename)
599
600 def test_register_threads(self):
601 self.check_register(all_threads=True)
602
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200603 def test_register_chain(self):
604 self.check_register(chain=True)
605
Victor Stinnere2d66902014-05-14 17:15:50 +0200606 @contextmanager
607 def check_stderr_none(self):
608 stderr = sys.stderr
609 try:
610 sys.stderr = None
611 with self.assertRaises(RuntimeError) as cm:
612 yield
613 self.assertEqual(str(cm.exception), "sys.stderr is None")
614 finally:
615 sys.stderr = stderr
616
617 def test_stderr_None(self):
618 # Issue #21497: provide an helpful error if sys.stderr is None,
619 # instead of just an attribute error: "None has no attribute fileno".
620 with self.check_stderr_none():
621 faulthandler.enable()
622 with self.check_stderr_none():
623 faulthandler.dump_traceback()
624 if hasattr(faulthandler, 'dump_traceback_later'):
625 with self.check_stderr_none():
626 faulthandler.dump_traceback_later(1e-3)
627 if hasattr(faulthandler, "register"):
628 with self.check_stderr_none():
629 faulthandler.register(signal.SIGUSR1)
630
Victor Stinner024e37a2011-03-31 01:31:06 +0200631
Victor Stinner024e37a2011-03-31 01:31:06 +0200632if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400633 unittest.main()