blob: e68a09e18119776f52182f6d335f1fb1bc426a86 [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
20
Victor Stinner44378d42011-04-01 15:37:12 +020021TIMEOUT = 0.5
22
Victor Stinner301f3f02011-06-01 13:49:12 +020023def expected_traceback(lineno1, lineno2, header, min_count=1):
Victor Stinner024e37a2011-03-31 01:31:06 +020024 regex = header
Victor Stinner7ad24e92011-03-31 22:35:49 +020025 regex += ' File "<string>", line %s in func\n' % lineno1
26 regex += ' File "<string>", line %s in <module>' % lineno2
Victor Stinner301f3f02011-06-01 13:49:12 +020027 if 1 < min_count:
28 return '^' + (regex + '\n') * (min_count - 1) + regex
29 else:
30 return '^' + regex + '$'
Victor Stinner024e37a2011-03-31 01:31:06 +020031
32@contextmanager
33def temporary_filename():
34 filename = tempfile.mktemp()
35 try:
36 yield filename
37 finally:
38 support.unlink(filename)
39
40class FaultHandlerTests(unittest.TestCase):
Victor Stinner05585cb2011-03-31 13:29:56 +020041 def get_output(self, code, filename=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020042 """
43 Run the specified code in Python (in a new child process) and read the
44 output from the standard error or from a file (if filename is set).
45 Return the output lines as a list.
46
47 Strip the reference count from the standard error for Python debug
48 build, and replace "Current thread 0x00007f8d8fbd9700" by "Current
49 thread XXX".
50 """
Victor Stinner6d201682014-08-10 19:50:08 +020051 code = dedent(code).strip()
Antoine Pitrou77e904e2013-10-08 23:04:32 +020052 with support.SuppressCrashReport():
53 process = script_helper.spawn_python('-c', code)
Victor Stinner024e37a2011-03-31 01:31:06 +020054 stdout, stderr = process.communicate()
55 exitcode = process.wait()
Victor Stinner19402332011-03-31 18:15:52 +020056 output = support.strip_python_stderr(stdout)
57 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020058 if filename:
Victor Stinner19402332011-03-31 18:15:52 +020059 self.assertEqual(output, '')
Victor Stinner024e37a2011-03-31 01:31:06 +020060 with open(filename, "rb") as fp:
61 output = fp.read()
Victor Stinner19402332011-03-31 18:15:52 +020062 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020063 output = re.sub('Current thread 0x[0-9a-f]+',
64 'Current thread XXX',
65 output)
Victor Stinner05585cb2011-03-31 13:29:56 +020066 return output.splitlines(), exitcode
Victor Stinner024e37a2011-03-31 01:31:06 +020067
68 def check_fatal_error(self, code, line_number, name_regex,
Victor Stinner7bba62f2011-05-07 12:43:00 +020069 filename=None, all_threads=True, other_regex=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020070 """
71 Check that the fault handler for fatal errors is enabled and check the
72 traceback from the child process output.
73
74 Raise an error if the output doesn't match the expected format.
75 """
76 if all_threads:
Guido van Rossum7be5d7d2013-10-20 18:21:02 -070077 header = 'Current thread XXX (most recent call first)'
Victor Stinner024e37a2011-03-31 01:31:06 +020078 else:
Guido van Rossum7be5d7d2013-10-20 18:21:02 -070079 header = 'Stack (most recent call first)'
Victor Stinner024e37a2011-03-31 01:31:06 +020080 regex = """
Victor Stinner6d201682014-08-10 19:50:08 +020081 ^Fatal Python error: {name}
Victor Stinner024e37a2011-03-31 01:31:06 +020082
Victor Stinner6d201682014-08-10 19:50:08 +020083 {header}:
84 File "<string>", line {lineno} in <module>
85 """
86 regex = dedent(regex.format(
Victor Stinner024e37a2011-03-31 01:31:06 +020087 lineno=line_number,
88 name=name_regex,
Victor Stinner6d201682014-08-10 19:50:08 +020089 header=re.escape(header))).strip()
Victor Stinnerf0480752011-03-31 11:34:08 +020090 if other_regex:
91 regex += '|' + other_regex
Antoine Pitrou77e904e2013-10-08 23:04:32 +020092 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +020093 output = '\n'.join(output)
94 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +020095 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +020096
Victor Stinner330426c2013-07-03 22:29:42 +020097 @unittest.skipIf(sys.platform.startswith('aix'),
98 "the first page of memory is a mapped read-only on AIX")
Victor Stinner024e37a2011-03-31 01:31:06 +020099 def test_read_null(self):
100 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200101 import faulthandler
102 faulthandler.enable()
103 faulthandler._read_null()
104 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200105 3,
Victor Stinnera8db3782011-08-08 22:43:45 +0200106 # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion
107 '(?:Segmentation fault|Bus error|Illegal instruction)')
Victor Stinner024e37a2011-03-31 01:31:06 +0200108
109 def test_sigsegv(self):
110 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200111 import faulthandler
112 faulthandler.enable()
113 faulthandler._sigsegv()
114 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200115 3,
116 'Segmentation fault')
117
Victor Stinnerd727e232011-04-01 12:13:55 +0200118 def test_sigabrt(self):
119 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200120 import faulthandler
121 faulthandler.enable()
122 faulthandler._sigabrt()
123 """,
Victor Stinnerd727e232011-04-01 12:13:55 +0200124 3,
125 'Aborted')
126
Victor Stinner024e37a2011-03-31 01:31:06 +0200127 @unittest.skipIf(sys.platform == 'win32',
128 "SIGFPE cannot be caught on Windows")
129 def test_sigfpe(self):
130 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200131 import faulthandler
132 faulthandler.enable()
133 faulthandler._sigfpe()
134 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200135 3,
136 'Floating point exception')
137
138 @unittest.skipIf(not hasattr(faulthandler, '_sigbus'),
139 "need faulthandler._sigbus()")
140 def test_sigbus(self):
141 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200142 import faulthandler
143 faulthandler.enable()
144 faulthandler._sigbus()
145 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200146 3,
147 'Bus error')
148
149 @unittest.skipIf(not hasattr(faulthandler, '_sigill'),
150 "need faulthandler._sigill()")
151 def test_sigill(self):
152 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200153 import faulthandler
154 faulthandler.enable()
155 faulthandler._sigill()
156 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200157 3,
158 'Illegal instruction')
159
160 def test_fatal_error(self):
161 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200162 import faulthandler
163 faulthandler._fatal_error(b'xyz')
164 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200165 2,
166 'xyz')
167
Charles-François Natali3391e642011-09-01 23:08:21 +0200168 @unittest.skipIf(sys.platform.startswith('openbsd') and HAVE_THREADS,
169 "Issue #12868: sigaltstack() doesn't work on "
170 "OpenBSD if Python is compiled with pthread")
Victor Stinner024e37a2011-03-31 01:31:06 +0200171 @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'),
172 'need faulthandler._stack_overflow()')
173 def test_stack_overflow(self):
174 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200175 import faulthandler
176 faulthandler.enable()
177 faulthandler._stack_overflow()
178 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200179 3,
Victor Stinnerf0480752011-03-31 11:34:08 +0200180 '(?:Segmentation fault|Bus error)',
181 other_regex='unable to raise a stack overflow')
Victor Stinner024e37a2011-03-31 01:31:06 +0200182
183 def test_gil_released(self):
184 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200185 import faulthandler
186 faulthandler.enable()
Victor Stinner50838282014-09-30 13:54:14 +0200187 faulthandler._sigsegv(True)
Victor Stinner6d201682014-08-10 19:50:08 +0200188 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200189 3,
Victor Stinner50838282014-09-30 13:54:14 +0200190 'Segmentation fault')
Victor Stinner024e37a2011-03-31 01:31:06 +0200191
192 def test_enable_file(self):
193 with temporary_filename() as filename:
194 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200195 import faulthandler
196 output = open({filename}, 'wb')
197 faulthandler.enable(output)
198 faulthandler._sigsegv()
199 """.format(filename=repr(filename)),
Victor Stinner024e37a2011-03-31 01:31:06 +0200200 4,
Victor Stinner56785392013-06-17 23:37:59 +0200201 'Segmentation fault',
Victor Stinner024e37a2011-03-31 01:31:06 +0200202 filename=filename)
203
Victor Stinner7bba62f2011-05-07 12:43:00 +0200204 def test_enable_single_thread(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200205 self.check_fatal_error("""
Victor Stinner6d201682014-08-10 19:50:08 +0200206 import faulthandler
207 faulthandler.enable(all_threads=False)
208 faulthandler._sigsegv()
209 """,
Victor Stinner024e37a2011-03-31 01:31:06 +0200210 3,
Victor Stinner56785392013-06-17 23:37:59 +0200211 'Segmentation fault',
Victor Stinner7bba62f2011-05-07 12:43:00 +0200212 all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200213
214 def test_disable(self):
215 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200216 import faulthandler
217 faulthandler.enable()
218 faulthandler.disable()
219 faulthandler._sigsegv()
220 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200221 not_expected = 'Fatal Python error'
Antoine Pitrou77e904e2013-10-08 23:04:32 +0200222 stderr, exitcode = self.get_output(code)
Victor Stinner29001c82014-09-25 00:38:48 +0200223 stderr = '\n'.join(stderr)
Victor Stinner024e37a2011-03-31 01:31:06 +0200224 self.assertTrue(not_expected not in stderr,
225 "%r is present in %r" % (not_expected, stderr))
Victor Stinner05585cb2011-03-31 13:29:56 +0200226 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200227
228 def test_is_enabled(self):
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200229 orig_stderr = sys.stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200230 try:
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200231 # regrtest may replace sys.stderr by io.StringIO object, but
232 # faulthandler.enable() requires that sys.stderr has a fileno()
233 # method
Victor Stinner72488502011-06-29 23:24:31 +0200234 sys.stderr = sys.__stderr__
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200235
236 was_enabled = faulthandler.is_enabled()
237 try:
Victor Stinner024e37a2011-03-31 01:31:06 +0200238 faulthandler.enable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200239 self.assertTrue(faulthandler.is_enabled())
Victor Stinner024e37a2011-03-31 01:31:06 +0200240 faulthandler.disable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200241 self.assertFalse(faulthandler.is_enabled())
242 finally:
243 if was_enabled:
244 faulthandler.enable()
245 else:
246 faulthandler.disable()
247 finally:
248 sys.stderr = orig_stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200249
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200250 def test_disabled_by_default(self):
251 # By default, the module should be disabled
252 code = "import faulthandler; print(faulthandler.is_enabled())"
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800253 args = filter(None, (sys.executable,
254 "-E" if sys.flags.ignore_environment else "",
255 "-c", code))
256 env = os.environ.copy()
257 env.pop("PYTHONFAULTHANDLER", None)
258 # don't use assert_python_ok() because it always enables faulthandler
259 output = subprocess.check_output(args, env=env)
Victor Stinner88983502013-09-08 11:36:23 +0200260 self.assertEqual(output.rstrip(), b"False")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200261
262 def test_sys_xoptions(self):
263 # Test python -X faulthandler
264 code = "import faulthandler; print(faulthandler.is_enabled())"
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800265 args = filter(None, (sys.executable,
266 "-E" if sys.flags.ignore_environment else "",
267 "-X", "faulthandler", "-c", code))
268 env = os.environ.copy()
269 env.pop("PYTHONFAULTHANDLER", None)
270 # don't use assert_python_ok() because it always enables faulthandler
271 output = subprocess.check_output(args, env=env)
Victor Stinner88983502013-09-08 11:36:23 +0200272 self.assertEqual(output.rstrip(), b"True")
273
274 def test_env_var(self):
275 # empty env var
276 code = "import faulthandler; print(faulthandler.is_enabled())"
277 args = (sys.executable, "-c", code)
278 env = os.environ.copy()
279 env['PYTHONFAULTHANDLER'] = ''
Gregory P. Smithfe7c5d62015-01-22 17:33:28 -0800280 # don't use assert_python_ok() because it always enables faulthandler
Victor Stinner88983502013-09-08 11:36:23 +0200281 output = subprocess.check_output(args, env=env)
282 self.assertEqual(output.rstrip(), b"False")
283
284 # non-empty env var
285 env = os.environ.copy()
286 env['PYTHONFAULTHANDLER'] = '1'
287 output = subprocess.check_output(args, env=env)
288 self.assertEqual(output.rstrip(), b"True")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200289
Victor Stinner024e37a2011-03-31 01:31:06 +0200290 def check_dump_traceback(self, filename):
291 """
292 Explicitly call dump_traceback() function and check its output.
293 Raise an error if the output doesn't match the expected format.
294 """
295 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200296 import faulthandler
Victor Stinner024e37a2011-03-31 01:31:06 +0200297
Victor Stinner6d201682014-08-10 19:50:08 +0200298 def funcB():
299 if {has_filename}:
300 with open({filename}, "wb") as fp:
301 faulthandler.dump_traceback(fp, all_threads=False)
302 else:
303 faulthandler.dump_traceback(all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200304
Victor Stinner6d201682014-08-10 19:50:08 +0200305 def funcA():
306 funcB()
Victor Stinner024e37a2011-03-31 01:31:06 +0200307
Victor Stinner6d201682014-08-10 19:50:08 +0200308 funcA()
309 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200310 code = code.format(
311 filename=repr(filename),
312 has_filename=bool(filename),
313 )
314 if filename:
315 lineno = 6
316 else:
317 lineno = 8
318 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700319 'Stack (most recent call first):',
Victor Stinner024e37a2011-03-31 01:31:06 +0200320 ' File "<string>", line %s in funcB' % lineno,
321 ' File "<string>", line 11 in funcA',
322 ' File "<string>", line 13 in <module>'
323 ]
Victor Stinner05585cb2011-03-31 13:29:56 +0200324 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200325 self.assertEqual(trace, expected)
Victor Stinner05585cb2011-03-31 13:29:56 +0200326 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200327
328 def test_dump_traceback(self):
329 self.check_dump_traceback(None)
Victor Stinner19402332011-03-31 18:15:52 +0200330
331 def test_dump_traceback_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200332 with temporary_filename() as filename:
333 self.check_dump_traceback(filename)
334
Victor Stinner53386d82012-08-01 19:45:34 +0200335 def test_truncate(self):
336 maxlen = 500
337 func_name = 'x' * (maxlen + 50)
338 truncated = 'x' * maxlen + '...'
339 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200340 import faulthandler
Victor Stinner53386d82012-08-01 19:45:34 +0200341
Victor Stinner6d201682014-08-10 19:50:08 +0200342 def {func_name}():
343 faulthandler.dump_traceback(all_threads=False)
Victor Stinner53386d82012-08-01 19:45:34 +0200344
Victor Stinner6d201682014-08-10 19:50:08 +0200345 {func_name}()
346 """
Victor Stinner53386d82012-08-01 19:45:34 +0200347 code = code.format(
348 func_name=func_name,
349 )
350 expected = [
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700351 'Stack (most recent call first):',
Victor Stinner53386d82012-08-01 19:45:34 +0200352 ' File "<string>", line 4 in %s' % truncated,
353 ' File "<string>", line 6 in <module>'
354 ]
355 trace, exitcode = self.get_output(code)
356 self.assertEqual(trace, expected)
357 self.assertEqual(exitcode, 0)
358
Victor Stinnerff4cd882011-04-07 11:50:25 +0200359 @unittest.skipIf(not HAVE_THREADS, 'need threads')
Victor Stinner024e37a2011-03-31 01:31:06 +0200360 def check_dump_traceback_threads(self, filename):
361 """
362 Call explicitly dump_traceback(all_threads=True) and check the output.
363 Raise an error if the output doesn't match the expected format.
364 """
365 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200366 import faulthandler
367 from threading import Thread, Event
368 import time
Victor Stinner024e37a2011-03-31 01:31:06 +0200369
Victor Stinner6d201682014-08-10 19:50:08 +0200370 def dump():
371 if {filename}:
372 with open({filename}, "wb") as fp:
373 faulthandler.dump_traceback(fp, all_threads=True)
374 else:
375 faulthandler.dump_traceback(all_threads=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200376
Victor Stinner6d201682014-08-10 19:50:08 +0200377 class Waiter(Thread):
378 # avoid blocking if the main thread raises an exception.
379 daemon = True
Victor Stinner024e37a2011-03-31 01:31:06 +0200380
Victor Stinner6d201682014-08-10 19:50:08 +0200381 def __init__(self):
382 Thread.__init__(self)
383 self.running = Event()
384 self.stop = Event()
Victor Stinner024e37a2011-03-31 01:31:06 +0200385
Victor Stinner6d201682014-08-10 19:50:08 +0200386 def run(self):
387 self.running.set()
388 self.stop.wait()
Victor Stinner024e37a2011-03-31 01:31:06 +0200389
Victor Stinner6d201682014-08-10 19:50:08 +0200390 waiter = Waiter()
391 waiter.start()
392 waiter.running.wait()
393 dump()
394 waiter.stop.set()
395 waiter.join()
396 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200397 code = code.format(filename=repr(filename))
Victor Stinner05585cb2011-03-31 13:29:56 +0200398 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200399 output = '\n'.join(output)
400 if filename:
401 lineno = 8
402 else:
403 lineno = 10
404 regex = """
Victor Stinner6d201682014-08-10 19:50:08 +0200405 ^Thread 0x[0-9a-f]+ \(most recent call first\):
406 (?: File ".*threading.py", line [0-9]+ in [_a-z]+
407 ){{1,3}} File "<string>", line 23 in run
408 File ".*threading.py", line [0-9]+ in _bootstrap_inner
409 File ".*threading.py", line [0-9]+ in _bootstrap
Victor Stinner024e37a2011-03-31 01:31:06 +0200410
Victor Stinner6d201682014-08-10 19:50:08 +0200411 Current thread XXX \(most recent call first\):
412 File "<string>", line {lineno} in dump
413 File "<string>", line 28 in <module>$
414 """
415 regex = dedent(regex.format(lineno=lineno)).strip()
Victor Stinner024e37a2011-03-31 01:31:06 +0200416 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200417 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200418
419 def test_dump_traceback_threads(self):
420 self.check_dump_traceback_threads(None)
Victor Stinner19402332011-03-31 18:15:52 +0200421
422 def test_dump_traceback_threads_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200423 with temporary_filename() as filename:
424 self.check_dump_traceback_threads(filename)
425
Georg Brandldeb92b52012-09-22 08:58:55 +0200426 def _check_dump_traceback_later(self, repeat, cancel, filename, loops):
Victor Stinner024e37a2011-03-31 01:31:06 +0200427 """
428 Check how many times the traceback is written in timeout x 2.5 seconds,
429 or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending
430 on repeat and cancel options.
431
432 Raise an error if the output doesn't match the expect format.
433 """
Victor Stinnerc790a532011-04-08 13:39:59 +0200434 timeout_str = str(datetime.timedelta(seconds=TIMEOUT))
Victor Stinner024e37a2011-03-31 01:31:06 +0200435 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200436 import faulthandler
437 import time
Victor Stinner024e37a2011-03-31 01:31:06 +0200438
Victor Stinner6d201682014-08-10 19:50:08 +0200439 def func(timeout, repeat, cancel, file, loops):
440 for loop in range(loops):
441 faulthandler.dump_traceback_later(timeout, repeat=repeat, file=file)
442 if cancel:
443 faulthandler.cancel_dump_traceback_later()
444 time.sleep(timeout * 5)
445 faulthandler.cancel_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200446
Victor Stinner6d201682014-08-10 19:50:08 +0200447 timeout = {timeout}
448 repeat = {repeat}
449 cancel = {cancel}
450 loops = {loops}
451 if {has_filename}:
452 file = open({filename}, "wb")
453 else:
454 file = None
455 func(timeout, repeat, cancel, file, loops)
456 if file is not None:
457 file.close()
458 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200459 code = code.format(
Victor Stinnerde10f402011-04-08 12:57:06 +0200460 timeout=TIMEOUT,
Victor Stinner024e37a2011-03-31 01:31:06 +0200461 repeat=repeat,
462 cancel=cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200463 loops=loops,
464 has_filename=bool(filename),
465 filename=repr(filename),
Victor Stinner024e37a2011-03-31 01:31:06 +0200466 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200467 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200468 trace = '\n'.join(trace)
469
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200470 if not cancel:
Victor Stinnerde10f402011-04-08 12:57:06 +0200471 count = loops
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200472 if repeat:
Victor Stinnerde10f402011-04-08 12:57:06 +0200473 count *= 2
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700474 header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+ \(most recent call first\):\n' % timeout_str
Victor Stinner301f3f02011-06-01 13:49:12 +0200475 regex = expected_traceback(9, 20, header, min_count=count)
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200476 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200477 else:
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200478 self.assertEqual(trace, '')
Victor Stinner05585cb2011-03-31 13:29:56 +0200479 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200480
Georg Brandldeb92b52012-09-22 08:58:55 +0200481 @unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'),
482 'need faulthandler.dump_traceback_later()')
483 def check_dump_traceback_later(self, repeat=False, cancel=False,
Victor Stinnerde10f402011-04-08 12:57:06 +0200484 file=False, twice=False):
485 if twice:
486 loops = 2
487 else:
488 loops = 1
Victor Stinner024e37a2011-03-31 01:31:06 +0200489 if file:
490 with temporary_filename() as filename:
Georg Brandldeb92b52012-09-22 08:58:55 +0200491 self._check_dump_traceback_later(repeat, cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200492 filename, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200493 else:
Georg Brandldeb92b52012-09-22 08:58:55 +0200494 self._check_dump_traceback_later(repeat, cancel, None, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200495
Georg Brandldeb92b52012-09-22 08:58:55 +0200496 def test_dump_traceback_later(self):
497 self.check_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200498
Georg Brandldeb92b52012-09-22 08:58:55 +0200499 def test_dump_traceback_later_repeat(self):
500 self.check_dump_traceback_later(repeat=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200501
Georg Brandldeb92b52012-09-22 08:58:55 +0200502 def test_dump_traceback_later_cancel(self):
503 self.check_dump_traceback_later(cancel=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200504
Georg Brandldeb92b52012-09-22 08:58:55 +0200505 def test_dump_traceback_later_file(self):
506 self.check_dump_traceback_later(file=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200507
Georg Brandldeb92b52012-09-22 08:58:55 +0200508 def test_dump_traceback_later_twice(self):
509 self.check_dump_traceback_later(twice=True)
Victor Stinnerde10f402011-04-08 12:57:06 +0200510
Victor Stinner024e37a2011-03-31 01:31:06 +0200511 @unittest.skipIf(not hasattr(faulthandler, "register"),
512 "need faulthandler.register")
Victor Stinnera01ca122011-04-01 12:56:17 +0200513 def check_register(self, filename=False, all_threads=False,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200514 unregister=False, chain=False):
Victor Stinner024e37a2011-03-31 01:31:06 +0200515 """
516 Register a handler displaying the traceback on a user signal. Raise the
517 signal and check the written traceback.
518
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200519 If chain is True, check that the previous signal handler is called.
520
Victor Stinner024e37a2011-03-31 01:31:06 +0200521 Raise an error if the output doesn't match the expected format.
522 """
Victor Stinnera01ca122011-04-01 12:56:17 +0200523 signum = signal.SIGUSR1
Victor Stinner024e37a2011-03-31 01:31:06 +0200524 code = """
Victor Stinner6d201682014-08-10 19:50:08 +0200525 import faulthandler
526 import os
527 import signal
528 import sys
Victor Stinner024e37a2011-03-31 01:31:06 +0200529
Victor Stinner6d201682014-08-10 19:50:08 +0200530 def func(signum):
531 os.kill(os.getpid(), signum)
Victor Stinner024e37a2011-03-31 01:31:06 +0200532
Victor Stinner6d201682014-08-10 19:50:08 +0200533 def handler(signum, frame):
534 handler.called = True
535 handler.called = False
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200536
Victor Stinner6d201682014-08-10 19:50:08 +0200537 exitcode = 0
538 signum = {signum}
539 unregister = {unregister}
540 chain = {chain}
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200541
Victor Stinner6d201682014-08-10 19:50:08 +0200542 if {has_filename}:
543 file = open({filename}, "wb")
544 else:
545 file = None
546 if chain:
547 signal.signal(signum, handler)
548 faulthandler.register(signum, file=file,
549 all_threads={all_threads}, chain={chain})
550 if unregister:
551 faulthandler.unregister(signum)
552 func(signum)
553 if chain and not handler.called:
554 if file is not None:
555 output = file
556 else:
557 output = sys.stderr
558 print("Error: signal handler not called!", file=output)
559 exitcode = 1
560 if file is not None:
561 file.close()
562 sys.exit(exitcode)
563 """
Victor Stinner024e37a2011-03-31 01:31:06 +0200564 code = code.format(
565 filename=repr(filename),
566 has_filename=bool(filename),
567 all_threads=all_threads,
Victor Stinnera01ca122011-04-01 12:56:17 +0200568 signum=signum,
569 unregister=unregister,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200570 chain=chain,
Victor Stinner024e37a2011-03-31 01:31:06 +0200571 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200572 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200573 trace = '\n'.join(trace)
Victor Stinnera01ca122011-04-01 12:56:17 +0200574 if not unregister:
575 if all_threads:
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700576 regex = 'Current thread XXX \(most recent call first\):\n'
Victor Stinnera01ca122011-04-01 12:56:17 +0200577 else:
Guido van Rossum7be5d7d2013-10-20 18:21:02 -0700578 regex = 'Stack \(most recent call first\):\n'
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200579 regex = expected_traceback(7, 28, regex)
Victor Stinnera01ca122011-04-01 12:56:17 +0200580 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200581 else:
Victor Stinnera01ca122011-04-01 12:56:17 +0200582 self.assertEqual(trace, '')
583 if unregister:
584 self.assertNotEqual(exitcode, 0)
585 else:
586 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200587
588 def test_register(self):
589 self.check_register()
590
Victor Stinnera01ca122011-04-01 12:56:17 +0200591 def test_unregister(self):
592 self.check_register(unregister=True)
593
Victor Stinner024e37a2011-03-31 01:31:06 +0200594 def test_register_file(self):
595 with temporary_filename() as filename:
596 self.check_register(filename=filename)
597
598 def test_register_threads(self):
599 self.check_register(all_threads=True)
600
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200601 def test_register_chain(self):
602 self.check_register(chain=True)
603
Victor Stinnere2d66902014-05-14 17:15:50 +0200604 @contextmanager
605 def check_stderr_none(self):
606 stderr = sys.stderr
607 try:
608 sys.stderr = None
609 with self.assertRaises(RuntimeError) as cm:
610 yield
611 self.assertEqual(str(cm.exception), "sys.stderr is None")
612 finally:
613 sys.stderr = stderr
614
615 def test_stderr_None(self):
616 # Issue #21497: provide an helpful error if sys.stderr is None,
617 # instead of just an attribute error: "None has no attribute fileno".
618 with self.check_stderr_none():
619 faulthandler.enable()
620 with self.check_stderr_none():
621 faulthandler.dump_traceback()
622 if hasattr(faulthandler, 'dump_traceback_later'):
623 with self.check_stderr_none():
624 faulthandler.dump_traceback_later(1e-3)
625 if hasattr(faulthandler, "register"):
626 with self.check_stderr_none():
627 faulthandler.register(signal.SIGUSR1)
628
Victor Stinner024e37a2011-03-31 01:31:06 +0200629
Victor Stinner024e37a2011-03-31 01:31:06 +0200630if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400631 unittest.main()