blob: 2d374b9d6c760a8cb7145622263aeafea4730cf2 [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
13
Victor Stinnerff4cd882011-04-07 11:50:25 +020014try:
15 import threading
16 HAVE_THREADS = True
17except ImportError:
18 HAVE_THREADS = False
19
Victor Stinner44378d42011-04-01 15:37:12 +020020TIMEOUT = 0.5
21
Victor Stinner301f3f02011-06-01 13:49:12 +020022def expected_traceback(lineno1, lineno2, header, min_count=1):
Victor Stinner024e37a2011-03-31 01:31:06 +020023 regex = header
Victor Stinner7ad24e92011-03-31 22:35:49 +020024 regex += ' File "<string>", line %s in func\n' % lineno1
25 regex += ' File "<string>", line %s in <module>' % lineno2
Victor Stinner301f3f02011-06-01 13:49:12 +020026 if 1 < min_count:
27 return '^' + (regex + '\n') * (min_count - 1) + regex
28 else:
29 return '^' + regex + '$'
Victor Stinner024e37a2011-03-31 01:31:06 +020030
31@contextmanager
32def temporary_filename():
33 filename = tempfile.mktemp()
34 try:
35 yield filename
36 finally:
37 support.unlink(filename)
38
39class FaultHandlerTests(unittest.TestCase):
Victor Stinner05585cb2011-03-31 13:29:56 +020040 def get_output(self, code, filename=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020041 """
42 Run the specified code in Python (in a new child process) and read the
43 output from the standard error or from a file (if filename is set).
44 Return the output lines as a list.
45
46 Strip the reference count from the standard error for Python debug
47 build, and replace "Current thread 0x00007f8d8fbd9700" by "Current
48 thread XXX".
49 """
Antoine Pitrou77e904e2013-10-08 23:04:32 +020050 with support.SuppressCrashReport():
51 process = script_helper.spawn_python('-c', code)
Victor Stinner024e37a2011-03-31 01:31:06 +020052 stdout, stderr = process.communicate()
53 exitcode = process.wait()
Victor Stinner19402332011-03-31 18:15:52 +020054 output = support.strip_python_stderr(stdout)
55 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020056 if filename:
Victor Stinner19402332011-03-31 18:15:52 +020057 self.assertEqual(output, '')
Victor Stinner024e37a2011-03-31 01:31:06 +020058 with open(filename, "rb") as fp:
59 output = fp.read()
Victor Stinner19402332011-03-31 18:15:52 +020060 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020061 output = re.sub('Current thread 0x[0-9a-f]+',
62 'Current thread XXX',
63 output)
Victor Stinner05585cb2011-03-31 13:29:56 +020064 return output.splitlines(), exitcode
Victor Stinner024e37a2011-03-31 01:31:06 +020065
66 def check_fatal_error(self, code, line_number, name_regex,
Victor Stinner7bba62f2011-05-07 12:43:00 +020067 filename=None, all_threads=True, other_regex=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020068 """
69 Check that the fault handler for fatal errors is enabled and check the
70 traceback from the child process output.
71
72 Raise an error if the output doesn't match the expected format.
73 """
74 if all_threads:
75 header = 'Current thread XXX'
76 else:
77 header = 'Traceback (most recent call first)'
78 regex = """
79^Fatal Python error: {name}
80
81{header}:
Vinay Sajip36371232012-05-06 11:28:46 +010082 File "<string>", line {lineno} in <module>
Victor Stinner024e37a2011-03-31 01:31:06 +020083""".strip()
84 regex = regex.format(
85 lineno=line_number,
86 name=name_regex,
87 header=re.escape(header))
Victor Stinnerf0480752011-03-31 11:34:08 +020088 if other_regex:
89 regex += '|' + other_regex
Antoine Pitrou77e904e2013-10-08 23:04:32 +020090 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +020091 output = '\n'.join(output)
92 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +020093 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +020094
Victor Stinner330426c2013-07-03 22:29:42 +020095 @unittest.skipIf(sys.platform.startswith('aix'),
96 "the first page of memory is a mapped read-only on AIX")
Victor Stinner024e37a2011-03-31 01:31:06 +020097 def test_read_null(self):
98 self.check_fatal_error("""
99import faulthandler
100faulthandler.enable()
101faulthandler._read_null()
102""".strip(),
103 3,
Victor Stinnera8db3782011-08-08 22:43:45 +0200104 # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion
105 '(?:Segmentation fault|Bus error|Illegal instruction)')
Victor Stinner024e37a2011-03-31 01:31:06 +0200106
107 def test_sigsegv(self):
108 self.check_fatal_error("""
109import faulthandler
110faulthandler.enable()
111faulthandler._sigsegv()
112""".strip(),
113 3,
114 'Segmentation fault')
115
Victor Stinnerd727e232011-04-01 12:13:55 +0200116 def test_sigabrt(self):
117 self.check_fatal_error("""
118import faulthandler
119faulthandler.enable()
120faulthandler._sigabrt()
121""".strip(),
122 3,
123 'Aborted')
124
Victor Stinner024e37a2011-03-31 01:31:06 +0200125 @unittest.skipIf(sys.platform == 'win32',
126 "SIGFPE cannot be caught on Windows")
127 def test_sigfpe(self):
128 self.check_fatal_error("""
129import faulthandler
130faulthandler.enable()
131faulthandler._sigfpe()
132""".strip(),
133 3,
134 'Floating point exception')
135
136 @unittest.skipIf(not hasattr(faulthandler, '_sigbus'),
137 "need faulthandler._sigbus()")
138 def test_sigbus(self):
139 self.check_fatal_error("""
140import faulthandler
141faulthandler.enable()
142faulthandler._sigbus()
143""".strip(),
144 3,
145 'Bus error')
146
147 @unittest.skipIf(not hasattr(faulthandler, '_sigill'),
148 "need faulthandler._sigill()")
149 def test_sigill(self):
150 self.check_fatal_error("""
151import faulthandler
152faulthandler.enable()
153faulthandler._sigill()
154""".strip(),
155 3,
156 'Illegal instruction')
157
158 def test_fatal_error(self):
159 self.check_fatal_error("""
160import faulthandler
161faulthandler._fatal_error(b'xyz')
162""".strip(),
163 2,
164 'xyz')
165
Charles-François Natali3391e642011-09-01 23:08:21 +0200166 @unittest.skipIf(sys.platform.startswith('openbsd') and HAVE_THREADS,
167 "Issue #12868: sigaltstack() doesn't work on "
168 "OpenBSD if Python is compiled with pthread")
Victor Stinner024e37a2011-03-31 01:31:06 +0200169 @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'),
170 'need faulthandler._stack_overflow()')
171 def test_stack_overflow(self):
172 self.check_fatal_error("""
173import faulthandler
174faulthandler.enable()
175faulthandler._stack_overflow()
176""".strip(),
177 3,
Victor Stinnerf0480752011-03-31 11:34:08 +0200178 '(?:Segmentation fault|Bus error)',
179 other_regex='unable to raise a stack overflow')
Victor Stinner024e37a2011-03-31 01:31:06 +0200180
181 def test_gil_released(self):
182 self.check_fatal_error("""
183import faulthandler
184faulthandler.enable()
185faulthandler._read_null(True)
186""".strip(),
187 3,
Victor Stinnera8db3782011-08-08 22:43:45 +0200188 '(?:Segmentation fault|Bus error|Illegal instruction)')
Victor Stinner024e37a2011-03-31 01:31:06 +0200189
190 def test_enable_file(self):
191 with temporary_filename() as filename:
192 self.check_fatal_error("""
193import faulthandler
194output = open({filename}, 'wb')
195faulthandler.enable(output)
Victor Stinner56785392013-06-17 23:37:59 +0200196faulthandler._sigsegv()
Victor Stinner024e37a2011-03-31 01:31:06 +0200197""".strip().format(filename=repr(filename)),
198 4,
Victor Stinner56785392013-06-17 23:37:59 +0200199 'Segmentation fault',
Victor Stinner024e37a2011-03-31 01:31:06 +0200200 filename=filename)
201
Victor Stinner7bba62f2011-05-07 12:43:00 +0200202 def test_enable_single_thread(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200203 self.check_fatal_error("""
204import faulthandler
Victor Stinner7bba62f2011-05-07 12:43:00 +0200205faulthandler.enable(all_threads=False)
Victor Stinner56785392013-06-17 23:37:59 +0200206faulthandler._sigsegv()
Victor Stinner024e37a2011-03-31 01:31:06 +0200207""".strip(),
208 3,
Victor Stinner56785392013-06-17 23:37:59 +0200209 'Segmentation fault',
Victor Stinner7bba62f2011-05-07 12:43:00 +0200210 all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200211
212 def test_disable(self):
213 code = """
214import faulthandler
215faulthandler.enable()
216faulthandler.disable()
Victor Stinner56785392013-06-17 23:37:59 +0200217faulthandler._sigsegv()
Victor Stinner024e37a2011-03-31 01:31:06 +0200218""".strip()
219 not_expected = 'Fatal Python error'
Antoine Pitrou77e904e2013-10-08 23:04:32 +0200220 stderr, exitcode = self.get_output(code)
Victor Stinner024e37a2011-03-31 01:31:06 +0200221 stder = '\n'.join(stderr)
222 self.assertTrue(not_expected not in stderr,
223 "%r is present in %r" % (not_expected, stderr))
Victor Stinner05585cb2011-03-31 13:29:56 +0200224 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200225
226 def test_is_enabled(self):
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200227 orig_stderr = sys.stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200228 try:
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200229 # regrtest may replace sys.stderr by io.StringIO object, but
230 # faulthandler.enable() requires that sys.stderr has a fileno()
231 # method
Victor Stinner72488502011-06-29 23:24:31 +0200232 sys.stderr = sys.__stderr__
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200233
234 was_enabled = faulthandler.is_enabled()
235 try:
Victor Stinner024e37a2011-03-31 01:31:06 +0200236 faulthandler.enable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200237 self.assertTrue(faulthandler.is_enabled())
Victor Stinner024e37a2011-03-31 01:31:06 +0200238 faulthandler.disable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200239 self.assertFalse(faulthandler.is_enabled())
240 finally:
241 if was_enabled:
242 faulthandler.enable()
243 else:
244 faulthandler.disable()
245 finally:
246 sys.stderr = orig_stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200247
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200248 def test_disabled_by_default(self):
249 # By default, the module should be disabled
250 code = "import faulthandler; print(faulthandler.is_enabled())"
Victor Stinneref8115e2013-06-25 21:54:17 +0200251 args = (sys.executable, '-E', '-c', code)
Victor Stinner88983502013-09-08 11:36:23 +0200252 # don't use assert_python_ok() because it always enable faulthandler
253 output = subprocess.check_output(args)
254 self.assertEqual(output.rstrip(), b"False")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200255
256 def test_sys_xoptions(self):
257 # Test python -X faulthandler
258 code = "import faulthandler; print(faulthandler.is_enabled())"
Victor Stinner88983502013-09-08 11:36:23 +0200259 args = (sys.executable, "-E", "-X", "faulthandler", "-c", code)
260 # don't use assert_python_ok() because it always enable faulthandler
261 output = subprocess.check_output(args)
262 self.assertEqual(output.rstrip(), b"True")
263
264 def test_env_var(self):
265 # empty env var
266 code = "import faulthandler; print(faulthandler.is_enabled())"
267 args = (sys.executable, "-c", code)
268 env = os.environ.copy()
269 env['PYTHONFAULTHANDLER'] = ''
270 # don't use assert_python_ok() because it always enable faulthandler
271 output = subprocess.check_output(args, env=env)
272 self.assertEqual(output.rstrip(), b"False")
273
274 # non-empty env var
275 env = os.environ.copy()
276 env['PYTHONFAULTHANDLER'] = '1'
277 output = subprocess.check_output(args, env=env)
278 self.assertEqual(output.rstrip(), b"True")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200279
Victor Stinner024e37a2011-03-31 01:31:06 +0200280 def check_dump_traceback(self, filename):
281 """
282 Explicitly call dump_traceback() function and check its output.
283 Raise an error if the output doesn't match the expected format.
284 """
285 code = """
286import faulthandler
287
288def funcB():
289 if {has_filename}:
290 with open({filename}, "wb") as fp:
Victor Stinner7bba62f2011-05-07 12:43:00 +0200291 faulthandler.dump_traceback(fp, all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200292 else:
Victor Stinner7bba62f2011-05-07 12:43:00 +0200293 faulthandler.dump_traceback(all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200294
295def funcA():
296 funcB()
297
298funcA()
299""".strip()
300 code = code.format(
301 filename=repr(filename),
302 has_filename=bool(filename),
303 )
304 if filename:
305 lineno = 6
306 else:
307 lineno = 8
308 expected = [
309 'Traceback (most recent call first):',
310 ' File "<string>", line %s in funcB' % lineno,
311 ' File "<string>", line 11 in funcA',
312 ' File "<string>", line 13 in <module>'
313 ]
Victor Stinner05585cb2011-03-31 13:29:56 +0200314 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200315 self.assertEqual(trace, expected)
Victor Stinner05585cb2011-03-31 13:29:56 +0200316 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200317
318 def test_dump_traceback(self):
319 self.check_dump_traceback(None)
Victor Stinner19402332011-03-31 18:15:52 +0200320
321 def test_dump_traceback_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200322 with temporary_filename() as filename:
323 self.check_dump_traceback(filename)
324
Victor Stinner53386d82012-08-01 19:45:34 +0200325 def test_truncate(self):
326 maxlen = 500
327 func_name = 'x' * (maxlen + 50)
328 truncated = 'x' * maxlen + '...'
329 code = """
330import faulthandler
331
332def {func_name}():
333 faulthandler.dump_traceback(all_threads=False)
334
335{func_name}()
336""".strip()
337 code = code.format(
338 func_name=func_name,
339 )
340 expected = [
341 'Traceback (most recent call first):',
342 ' File "<string>", line 4 in %s' % truncated,
343 ' File "<string>", line 6 in <module>'
344 ]
345 trace, exitcode = self.get_output(code)
346 self.assertEqual(trace, expected)
347 self.assertEqual(exitcode, 0)
348
Victor Stinnerff4cd882011-04-07 11:50:25 +0200349 @unittest.skipIf(not HAVE_THREADS, 'need threads')
Victor Stinner024e37a2011-03-31 01:31:06 +0200350 def check_dump_traceback_threads(self, filename):
351 """
352 Call explicitly dump_traceback(all_threads=True) and check the output.
353 Raise an error if the output doesn't match the expected format.
354 """
355 code = """
356import faulthandler
357from threading import Thread, Event
358import time
359
360def dump():
361 if {filename}:
362 with open({filename}, "wb") as fp:
363 faulthandler.dump_traceback(fp, all_threads=True)
364 else:
365 faulthandler.dump_traceback(all_threads=True)
366
367class Waiter(Thread):
368 # avoid blocking if the main thread raises an exception.
369 daemon = True
370
371 def __init__(self):
372 Thread.__init__(self)
373 self.running = Event()
374 self.stop = Event()
375
376 def run(self):
377 self.running.set()
378 self.stop.wait()
379
380waiter = Waiter()
381waiter.start()
382waiter.running.wait()
383dump()
384waiter.stop.set()
385waiter.join()
386""".strip()
387 code = code.format(filename=repr(filename))
Victor Stinner05585cb2011-03-31 13:29:56 +0200388 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200389 output = '\n'.join(output)
390 if filename:
391 lineno = 8
392 else:
393 lineno = 10
394 regex = """
395^Thread 0x[0-9a-f]+:
Victor Stinner1b3241f2011-04-03 18:41:22 +0200396(?: File ".*threading.py", line [0-9]+ in [_a-z]+
397){{1,3}} File "<string>", line 23 in run
Victor Stinner024e37a2011-03-31 01:31:06 +0200398 File ".*threading.py", line [0-9]+ in _bootstrap_inner
399 File ".*threading.py", line [0-9]+ in _bootstrap
400
401Current thread XXX:
402 File "<string>", line {lineno} in dump
403 File "<string>", line 28 in <module>$
404""".strip()
405 regex = regex.format(lineno=lineno)
406 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200407 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200408
409 def test_dump_traceback_threads(self):
410 self.check_dump_traceback_threads(None)
Victor Stinner19402332011-03-31 18:15:52 +0200411
412 def test_dump_traceback_threads_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200413 with temporary_filename() as filename:
414 self.check_dump_traceback_threads(filename)
415
Georg Brandldeb92b52012-09-22 08:58:55 +0200416 def _check_dump_traceback_later(self, repeat, cancel, filename, loops):
Victor Stinner024e37a2011-03-31 01:31:06 +0200417 """
418 Check how many times the traceback is written in timeout x 2.5 seconds,
419 or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending
420 on repeat and cancel options.
421
422 Raise an error if the output doesn't match the expect format.
423 """
Victor Stinnerc790a532011-04-08 13:39:59 +0200424 timeout_str = str(datetime.timedelta(seconds=TIMEOUT))
Victor Stinner024e37a2011-03-31 01:31:06 +0200425 code = """
426import faulthandler
427import time
428
Victor Stinnerde10f402011-04-08 12:57:06 +0200429def func(timeout, repeat, cancel, file, loops):
430 for loop in range(loops):
Georg Brandldeb92b52012-09-22 08:58:55 +0200431 faulthandler.dump_traceback_later(timeout, repeat=repeat, file=file)
Victor Stinnerde10f402011-04-08 12:57:06 +0200432 if cancel:
Georg Brandldeb92b52012-09-22 08:58:55 +0200433 faulthandler.cancel_dump_traceback_later()
Victor Stinner301f3f02011-06-01 13:49:12 +0200434 time.sleep(timeout * 5)
Georg Brandldeb92b52012-09-22 08:58:55 +0200435 faulthandler.cancel_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200436
Victor Stinner44378d42011-04-01 15:37:12 +0200437timeout = {timeout}
Victor Stinner024e37a2011-03-31 01:31:06 +0200438repeat = {repeat}
439cancel = {cancel}
Victor Stinnerde10f402011-04-08 12:57:06 +0200440loops = {loops}
Victor Stinner024e37a2011-03-31 01:31:06 +0200441if {has_filename}:
442 file = open({filename}, "wb")
443else:
444 file = None
Victor Stinnerde10f402011-04-08 12:57:06 +0200445func(timeout, repeat, cancel, file, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200446if file is not None:
447 file.close()
448""".strip()
449 code = code.format(
Victor Stinnerde10f402011-04-08 12:57:06 +0200450 timeout=TIMEOUT,
Victor Stinner024e37a2011-03-31 01:31:06 +0200451 repeat=repeat,
452 cancel=cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200453 loops=loops,
454 has_filename=bool(filename),
455 filename=repr(filename),
Victor Stinner024e37a2011-03-31 01:31:06 +0200456 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200457 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200458 trace = '\n'.join(trace)
459
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200460 if not cancel:
Victor Stinnerde10f402011-04-08 12:57:06 +0200461 count = loops
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200462 if repeat:
Victor Stinnerde10f402011-04-08 12:57:06 +0200463 count *= 2
Victor Stinnerc790a532011-04-08 13:39:59 +0200464 header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+:\n' % timeout_str
Victor Stinner301f3f02011-06-01 13:49:12 +0200465 regex = expected_traceback(9, 20, header, min_count=count)
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200466 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200467 else:
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200468 self.assertEqual(trace, '')
Victor Stinner05585cb2011-03-31 13:29:56 +0200469 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200470
Georg Brandldeb92b52012-09-22 08:58:55 +0200471 @unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'),
472 'need faulthandler.dump_traceback_later()')
473 def check_dump_traceback_later(self, repeat=False, cancel=False,
Victor Stinnerde10f402011-04-08 12:57:06 +0200474 file=False, twice=False):
475 if twice:
476 loops = 2
477 else:
478 loops = 1
Victor Stinner024e37a2011-03-31 01:31:06 +0200479 if file:
480 with temporary_filename() as filename:
Georg Brandldeb92b52012-09-22 08:58:55 +0200481 self._check_dump_traceback_later(repeat, cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200482 filename, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200483 else:
Georg Brandldeb92b52012-09-22 08:58:55 +0200484 self._check_dump_traceback_later(repeat, cancel, None, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200485
Georg Brandldeb92b52012-09-22 08:58:55 +0200486 def test_dump_traceback_later(self):
487 self.check_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200488
Georg Brandldeb92b52012-09-22 08:58:55 +0200489 def test_dump_traceback_later_repeat(self):
490 self.check_dump_traceback_later(repeat=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200491
Georg Brandldeb92b52012-09-22 08:58:55 +0200492 def test_dump_traceback_later_cancel(self):
493 self.check_dump_traceback_later(cancel=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200494
Georg Brandldeb92b52012-09-22 08:58:55 +0200495 def test_dump_traceback_later_file(self):
496 self.check_dump_traceback_later(file=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200497
Georg Brandldeb92b52012-09-22 08:58:55 +0200498 def test_dump_traceback_later_twice(self):
499 self.check_dump_traceback_later(twice=True)
Victor Stinnerde10f402011-04-08 12:57:06 +0200500
Victor Stinner024e37a2011-03-31 01:31:06 +0200501 @unittest.skipIf(not hasattr(faulthandler, "register"),
502 "need faulthandler.register")
Victor Stinnera01ca122011-04-01 12:56:17 +0200503 def check_register(self, filename=False, all_threads=False,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200504 unregister=False, chain=False):
Victor Stinner024e37a2011-03-31 01:31:06 +0200505 """
506 Register a handler displaying the traceback on a user signal. Raise the
507 signal and check the written traceback.
508
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200509 If chain is True, check that the previous signal handler is called.
510
Victor Stinner024e37a2011-03-31 01:31:06 +0200511 Raise an error if the output doesn't match the expected format.
512 """
Victor Stinnera01ca122011-04-01 12:56:17 +0200513 signum = signal.SIGUSR1
Victor Stinner024e37a2011-03-31 01:31:06 +0200514 code = """
515import faulthandler
516import os
517import signal
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200518import sys
Victor Stinner024e37a2011-03-31 01:31:06 +0200519
520def func(signum):
521 os.kill(os.getpid(), signum)
522
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200523def handler(signum, frame):
524 handler.called = True
525handler.called = False
526
527exitcode = 0
Victor Stinnera01ca122011-04-01 12:56:17 +0200528signum = {signum}
529unregister = {unregister}
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200530chain = {chain}
531
Victor Stinner024e37a2011-03-31 01:31:06 +0200532if {has_filename}:
533 file = open({filename}, "wb")
534else:
535 file = None
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200536if chain:
537 signal.signal(signum, handler)
538faulthandler.register(signum, file=file,
539 all_threads={all_threads}, chain={chain})
Victor Stinnera01ca122011-04-01 12:56:17 +0200540if unregister:
541 faulthandler.unregister(signum)
Victor Stinner024e37a2011-03-31 01:31:06 +0200542func(signum)
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200543if chain and not handler.called:
544 if file is not None:
545 output = file
546 else:
547 output = sys.stderr
548 print("Error: signal handler not called!", file=output)
549 exitcode = 1
Victor Stinner024e37a2011-03-31 01:31:06 +0200550if file is not None:
551 file.close()
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200552sys.exit(exitcode)
Victor Stinner024e37a2011-03-31 01:31:06 +0200553""".strip()
554 code = code.format(
555 filename=repr(filename),
556 has_filename=bool(filename),
557 all_threads=all_threads,
Victor Stinnera01ca122011-04-01 12:56:17 +0200558 signum=signum,
559 unregister=unregister,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200560 chain=chain,
Victor Stinner024e37a2011-03-31 01:31:06 +0200561 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200562 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200563 trace = '\n'.join(trace)
Victor Stinnera01ca122011-04-01 12:56:17 +0200564 if not unregister:
565 if all_threads:
566 regex = 'Current thread XXX:\n'
567 else:
568 regex = 'Traceback \(most recent call first\):\n'
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200569 regex = expected_traceback(7, 28, regex)
Victor Stinnera01ca122011-04-01 12:56:17 +0200570 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200571 else:
Victor Stinnera01ca122011-04-01 12:56:17 +0200572 self.assertEqual(trace, '')
573 if unregister:
574 self.assertNotEqual(exitcode, 0)
575 else:
576 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200577
578 def test_register(self):
579 self.check_register()
580
Victor Stinnera01ca122011-04-01 12:56:17 +0200581 def test_unregister(self):
582 self.check_register(unregister=True)
583
Victor Stinner024e37a2011-03-31 01:31:06 +0200584 def test_register_file(self):
585 with temporary_filename() as filename:
586 self.check_register(filename=filename)
587
588 def test_register_threads(self):
589 self.check_register(all_threads=True)
590
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200591 def test_register_chain(self):
592 self.check_register(chain=True)
593
Victor Stinner024e37a2011-03-31 01:31:06 +0200594
Victor Stinner024e37a2011-03-31 01:31:06 +0200595if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400596 unittest.main()