blob: 2ddc7bc51ade3fc64a8cb225b2f84cae05753746 [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 Stinner024e37a2011-03-31 01:31:06 +020022try:
23 from resource import setrlimit, RLIMIT_CORE, error as resource_error
24except ImportError:
25 prepare_subprocess = None
26else:
27 def prepare_subprocess():
28 # don't create core file
29 try:
30 setrlimit(RLIMIT_CORE, (0, 0))
31 except (ValueError, resource_error):
32 pass
33
Victor Stinner301f3f02011-06-01 13:49:12 +020034def expected_traceback(lineno1, lineno2, header, min_count=1):
Victor Stinner024e37a2011-03-31 01:31:06 +020035 regex = header
Victor Stinner7ad24e92011-03-31 22:35:49 +020036 regex += ' File "<string>", line %s in func\n' % lineno1
37 regex += ' File "<string>", line %s in <module>' % lineno2
Victor Stinner301f3f02011-06-01 13:49:12 +020038 if 1 < min_count:
39 return '^' + (regex + '\n') * (min_count - 1) + regex
40 else:
41 return '^' + regex + '$'
Victor Stinner024e37a2011-03-31 01:31:06 +020042
43@contextmanager
44def temporary_filename():
45 filename = tempfile.mktemp()
46 try:
47 yield filename
48 finally:
49 support.unlink(filename)
50
51class FaultHandlerTests(unittest.TestCase):
Victor Stinner05585cb2011-03-31 13:29:56 +020052 def get_output(self, code, filename=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020053 """
54 Run the specified code in Python (in a new child process) and read the
55 output from the standard error or from a file (if filename is set).
56 Return the output lines as a list.
57
58 Strip the reference count from the standard error for Python debug
59 build, and replace "Current thread 0x00007f8d8fbd9700" by "Current
60 thread XXX".
61 """
62 options = {}
63 if prepare_subprocess:
64 options['preexec_fn'] = prepare_subprocess
65 process = script_helper.spawn_python('-c', code, **options)
66 stdout, stderr = process.communicate()
67 exitcode = process.wait()
Victor Stinner19402332011-03-31 18:15:52 +020068 output = support.strip_python_stderr(stdout)
69 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020070 if filename:
Victor Stinner19402332011-03-31 18:15:52 +020071 self.assertEqual(output, '')
Victor Stinner024e37a2011-03-31 01:31:06 +020072 with open(filename, "rb") as fp:
73 output = fp.read()
Victor Stinner19402332011-03-31 18:15:52 +020074 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020075 output = re.sub('Current thread 0x[0-9a-f]+',
76 'Current thread XXX',
77 output)
Victor Stinner05585cb2011-03-31 13:29:56 +020078 return output.splitlines(), exitcode
Victor Stinner024e37a2011-03-31 01:31:06 +020079
80 def check_fatal_error(self, code, line_number, name_regex,
Victor Stinner7bba62f2011-05-07 12:43:00 +020081 filename=None, all_threads=True, other_regex=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020082 """
83 Check that the fault handler for fatal errors is enabled and check the
84 traceback from the child process output.
85
86 Raise an error if the output doesn't match the expected format.
87 """
88 if all_threads:
89 header = 'Current thread XXX'
90 else:
91 header = 'Traceback (most recent call first)'
92 regex = """
93^Fatal Python error: {name}
94
95{header}:
Vinay Sajip36371232012-05-06 11:28:46 +010096 File "<string>", line {lineno} in <module>
Victor Stinner024e37a2011-03-31 01:31:06 +020097""".strip()
98 regex = regex.format(
99 lineno=line_number,
100 name=name_regex,
101 header=re.escape(header))
Victor Stinnerf0480752011-03-31 11:34:08 +0200102 if other_regex:
103 regex += '|' + other_regex
Ezio Melotti25a40452013-03-05 20:26:17 +0200104 with support.suppress_crash_popup():
Ezio Melottie1857d92013-03-05 20:31:34 +0200105 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200106 output = '\n'.join(output)
107 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200108 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200109
110 def test_read_null(self):
111 self.check_fatal_error("""
112import faulthandler
113faulthandler.enable()
114faulthandler._read_null()
115""".strip(),
116 3,
Victor Stinnera8db3782011-08-08 22:43:45 +0200117 # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion
118 '(?:Segmentation fault|Bus error|Illegal instruction)')
Victor Stinner024e37a2011-03-31 01:31:06 +0200119
120 def test_sigsegv(self):
121 self.check_fatal_error("""
122import faulthandler
123faulthandler.enable()
124faulthandler._sigsegv()
125""".strip(),
126 3,
127 'Segmentation fault')
128
Victor Stinnerd727e232011-04-01 12:13:55 +0200129 def test_sigabrt(self):
130 self.check_fatal_error("""
131import faulthandler
132faulthandler.enable()
133faulthandler._sigabrt()
134""".strip(),
135 3,
136 'Aborted')
137
Victor Stinner024e37a2011-03-31 01:31:06 +0200138 @unittest.skipIf(sys.platform == 'win32',
139 "SIGFPE cannot be caught on Windows")
140 def test_sigfpe(self):
141 self.check_fatal_error("""
142import faulthandler
143faulthandler.enable()
144faulthandler._sigfpe()
145""".strip(),
146 3,
147 'Floating point exception')
148
149 @unittest.skipIf(not hasattr(faulthandler, '_sigbus'),
150 "need faulthandler._sigbus()")
151 def test_sigbus(self):
152 self.check_fatal_error("""
153import faulthandler
154faulthandler.enable()
155faulthandler._sigbus()
156""".strip(),
157 3,
158 'Bus error')
159
160 @unittest.skipIf(not hasattr(faulthandler, '_sigill'),
161 "need faulthandler._sigill()")
162 def test_sigill(self):
163 self.check_fatal_error("""
164import faulthandler
165faulthandler.enable()
166faulthandler._sigill()
167""".strip(),
168 3,
169 'Illegal instruction')
170
171 def test_fatal_error(self):
172 self.check_fatal_error("""
173import faulthandler
174faulthandler._fatal_error(b'xyz')
175""".strip(),
176 2,
177 'xyz')
178
Charles-François Natali3391e642011-09-01 23:08:21 +0200179 @unittest.skipIf(sys.platform.startswith('openbsd') and HAVE_THREADS,
180 "Issue #12868: sigaltstack() doesn't work on "
181 "OpenBSD if Python is compiled with pthread")
Victor Stinner024e37a2011-03-31 01:31:06 +0200182 @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'),
183 'need faulthandler._stack_overflow()')
184 def test_stack_overflow(self):
185 self.check_fatal_error("""
186import faulthandler
187faulthandler.enable()
188faulthandler._stack_overflow()
189""".strip(),
190 3,
Victor Stinnerf0480752011-03-31 11:34:08 +0200191 '(?:Segmentation fault|Bus error)',
192 other_regex='unable to raise a stack overflow')
Victor Stinner024e37a2011-03-31 01:31:06 +0200193
194 def test_gil_released(self):
195 self.check_fatal_error("""
196import faulthandler
197faulthandler.enable()
198faulthandler._read_null(True)
199""".strip(),
200 3,
Victor Stinnera8db3782011-08-08 22:43:45 +0200201 '(?:Segmentation fault|Bus error|Illegal instruction)')
Victor Stinner024e37a2011-03-31 01:31:06 +0200202
203 def test_enable_file(self):
204 with temporary_filename() as filename:
205 self.check_fatal_error("""
206import faulthandler
207output = open({filename}, 'wb')
208faulthandler.enable(output)
Victor Stinner56785392013-06-17 23:37:59 +0200209faulthandler._sigsegv()
Victor Stinner024e37a2011-03-31 01:31:06 +0200210""".strip().format(filename=repr(filename)),
211 4,
Victor Stinner56785392013-06-17 23:37:59 +0200212 'Segmentation fault',
Victor Stinner024e37a2011-03-31 01:31:06 +0200213 filename=filename)
214
Victor Stinner7bba62f2011-05-07 12:43:00 +0200215 def test_enable_single_thread(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200216 self.check_fatal_error("""
217import faulthandler
Victor Stinner7bba62f2011-05-07 12:43:00 +0200218faulthandler.enable(all_threads=False)
Victor Stinner56785392013-06-17 23:37:59 +0200219faulthandler._sigsegv()
Victor Stinner024e37a2011-03-31 01:31:06 +0200220""".strip(),
221 3,
Victor Stinner56785392013-06-17 23:37:59 +0200222 'Segmentation fault',
Victor Stinner7bba62f2011-05-07 12:43:00 +0200223 all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200224
225 def test_disable(self):
226 code = """
227import faulthandler
228faulthandler.enable()
229faulthandler.disable()
Victor Stinner56785392013-06-17 23:37:59 +0200230faulthandler._sigsegv()
Victor Stinner024e37a2011-03-31 01:31:06 +0200231""".strip()
232 not_expected = 'Fatal Python error'
Ezio Melotti25a40452013-03-05 20:26:17 +0200233 with support.suppress_crash_popup():
234 stderr, exitcode = self.get_output(code)
Victor Stinner024e37a2011-03-31 01:31:06 +0200235 stder = '\n'.join(stderr)
236 self.assertTrue(not_expected not in stderr,
237 "%r is present in %r" % (not_expected, stderr))
Victor Stinner05585cb2011-03-31 13:29:56 +0200238 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200239
240 def test_is_enabled(self):
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200241 orig_stderr = sys.stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200242 try:
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200243 # regrtest may replace sys.stderr by io.StringIO object, but
244 # faulthandler.enable() requires that sys.stderr has a fileno()
245 # method
Victor Stinner72488502011-06-29 23:24:31 +0200246 sys.stderr = sys.__stderr__
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200247
248 was_enabled = faulthandler.is_enabled()
249 try:
Victor Stinner024e37a2011-03-31 01:31:06 +0200250 faulthandler.enable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200251 self.assertTrue(faulthandler.is_enabled())
Victor Stinner024e37a2011-03-31 01:31:06 +0200252 faulthandler.disable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200253 self.assertFalse(faulthandler.is_enabled())
254 finally:
255 if was_enabled:
256 faulthandler.enable()
257 else:
258 faulthandler.disable()
259 finally:
260 sys.stderr = orig_stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200261
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200262 def test_disabled_by_default(self):
263 # By default, the module should be disabled
264 code = "import faulthandler; print(faulthandler.is_enabled())"
265 rc, stdout, stderr = assert_python_ok("-c", code)
266 stdout = (stdout + stderr).strip()
267 self.assertEqual(stdout, b"False")
268
269 def test_sys_xoptions(self):
270 # Test python -X faulthandler
271 code = "import faulthandler; print(faulthandler.is_enabled())"
272 rc, stdout, stderr = assert_python_ok("-X", "faulthandler", "-c", code)
273 stdout = (stdout + stderr).strip()
274 self.assertEqual(stdout, b"True")
275
Victor Stinner024e37a2011-03-31 01:31:06 +0200276 def check_dump_traceback(self, filename):
277 """
278 Explicitly call dump_traceback() function and check its output.
279 Raise an error if the output doesn't match the expected format.
280 """
281 code = """
282import faulthandler
283
284def funcB():
285 if {has_filename}:
286 with open({filename}, "wb") as fp:
Victor Stinner7bba62f2011-05-07 12:43:00 +0200287 faulthandler.dump_traceback(fp, all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200288 else:
Victor Stinner7bba62f2011-05-07 12:43:00 +0200289 faulthandler.dump_traceback(all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200290
291def funcA():
292 funcB()
293
294funcA()
295""".strip()
296 code = code.format(
297 filename=repr(filename),
298 has_filename=bool(filename),
299 )
300 if filename:
301 lineno = 6
302 else:
303 lineno = 8
304 expected = [
305 'Traceback (most recent call first):',
306 ' File "<string>", line %s in funcB' % lineno,
307 ' File "<string>", line 11 in funcA',
308 ' File "<string>", line 13 in <module>'
309 ]
Victor Stinner05585cb2011-03-31 13:29:56 +0200310 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200311 self.assertEqual(trace, expected)
Victor Stinner05585cb2011-03-31 13:29:56 +0200312 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200313
314 def test_dump_traceback(self):
315 self.check_dump_traceback(None)
Victor Stinner19402332011-03-31 18:15:52 +0200316
317 def test_dump_traceback_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200318 with temporary_filename() as filename:
319 self.check_dump_traceback(filename)
320
Victor Stinner53386d82012-08-01 19:45:34 +0200321 def test_truncate(self):
322 maxlen = 500
323 func_name = 'x' * (maxlen + 50)
324 truncated = 'x' * maxlen + '...'
325 code = """
326import faulthandler
327
328def {func_name}():
329 faulthandler.dump_traceback(all_threads=False)
330
331{func_name}()
332""".strip()
333 code = code.format(
334 func_name=func_name,
335 )
336 expected = [
337 'Traceback (most recent call first):',
338 ' File "<string>", line 4 in %s' % truncated,
339 ' File "<string>", line 6 in <module>'
340 ]
341 trace, exitcode = self.get_output(code)
342 self.assertEqual(trace, expected)
343 self.assertEqual(exitcode, 0)
344
Victor Stinnerff4cd882011-04-07 11:50:25 +0200345 @unittest.skipIf(not HAVE_THREADS, 'need threads')
Victor Stinner024e37a2011-03-31 01:31:06 +0200346 def check_dump_traceback_threads(self, filename):
347 """
348 Call explicitly dump_traceback(all_threads=True) and check the output.
349 Raise an error if the output doesn't match the expected format.
350 """
351 code = """
352import faulthandler
353from threading import Thread, Event
354import time
355
356def dump():
357 if {filename}:
358 with open({filename}, "wb") as fp:
359 faulthandler.dump_traceback(fp, all_threads=True)
360 else:
361 faulthandler.dump_traceback(all_threads=True)
362
363class Waiter(Thread):
364 # avoid blocking if the main thread raises an exception.
365 daemon = True
366
367 def __init__(self):
368 Thread.__init__(self)
369 self.running = Event()
370 self.stop = Event()
371
372 def run(self):
373 self.running.set()
374 self.stop.wait()
375
376waiter = Waiter()
377waiter.start()
378waiter.running.wait()
379dump()
380waiter.stop.set()
381waiter.join()
382""".strip()
383 code = code.format(filename=repr(filename))
Victor Stinner05585cb2011-03-31 13:29:56 +0200384 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200385 output = '\n'.join(output)
386 if filename:
387 lineno = 8
388 else:
389 lineno = 10
390 regex = """
391^Thread 0x[0-9a-f]+:
Victor Stinner1b3241f2011-04-03 18:41:22 +0200392(?: File ".*threading.py", line [0-9]+ in [_a-z]+
393){{1,3}} File "<string>", line 23 in run
Victor Stinner024e37a2011-03-31 01:31:06 +0200394 File ".*threading.py", line [0-9]+ in _bootstrap_inner
395 File ".*threading.py", line [0-9]+ in _bootstrap
396
397Current thread XXX:
398 File "<string>", line {lineno} in dump
399 File "<string>", line 28 in <module>$
400""".strip()
401 regex = regex.format(lineno=lineno)
402 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200403 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200404
405 def test_dump_traceback_threads(self):
406 self.check_dump_traceback_threads(None)
Victor Stinner19402332011-03-31 18:15:52 +0200407
408 def test_dump_traceback_threads_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200409 with temporary_filename() as filename:
410 self.check_dump_traceback_threads(filename)
411
Georg Brandldeb92b52012-09-22 08:58:55 +0200412 def _check_dump_traceback_later(self, repeat, cancel, filename, loops):
Victor Stinner024e37a2011-03-31 01:31:06 +0200413 """
414 Check how many times the traceback is written in timeout x 2.5 seconds,
415 or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending
416 on repeat and cancel options.
417
418 Raise an error if the output doesn't match the expect format.
419 """
Victor Stinnerc790a532011-04-08 13:39:59 +0200420 timeout_str = str(datetime.timedelta(seconds=TIMEOUT))
Victor Stinner024e37a2011-03-31 01:31:06 +0200421 code = """
422import faulthandler
423import time
424
Victor Stinnerde10f402011-04-08 12:57:06 +0200425def func(timeout, repeat, cancel, file, loops):
426 for loop in range(loops):
Georg Brandldeb92b52012-09-22 08:58:55 +0200427 faulthandler.dump_traceback_later(timeout, repeat=repeat, file=file)
Victor Stinnerde10f402011-04-08 12:57:06 +0200428 if cancel:
Georg Brandldeb92b52012-09-22 08:58:55 +0200429 faulthandler.cancel_dump_traceback_later()
Victor Stinner301f3f02011-06-01 13:49:12 +0200430 time.sleep(timeout * 5)
Georg Brandldeb92b52012-09-22 08:58:55 +0200431 faulthandler.cancel_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200432
Victor Stinner44378d42011-04-01 15:37:12 +0200433timeout = {timeout}
Victor Stinner024e37a2011-03-31 01:31:06 +0200434repeat = {repeat}
435cancel = {cancel}
Victor Stinnerde10f402011-04-08 12:57:06 +0200436loops = {loops}
Victor Stinner024e37a2011-03-31 01:31:06 +0200437if {has_filename}:
438 file = open({filename}, "wb")
439else:
440 file = None
Victor Stinnerde10f402011-04-08 12:57:06 +0200441func(timeout, repeat, cancel, file, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200442if file is not None:
443 file.close()
444""".strip()
445 code = code.format(
Victor Stinnerde10f402011-04-08 12:57:06 +0200446 timeout=TIMEOUT,
Victor Stinner024e37a2011-03-31 01:31:06 +0200447 repeat=repeat,
448 cancel=cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200449 loops=loops,
450 has_filename=bool(filename),
451 filename=repr(filename),
Victor Stinner024e37a2011-03-31 01:31:06 +0200452 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200453 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200454 trace = '\n'.join(trace)
455
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200456 if not cancel:
Victor Stinnerde10f402011-04-08 12:57:06 +0200457 count = loops
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200458 if repeat:
Victor Stinnerde10f402011-04-08 12:57:06 +0200459 count *= 2
Victor Stinnerc790a532011-04-08 13:39:59 +0200460 header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+:\n' % timeout_str
Victor Stinner301f3f02011-06-01 13:49:12 +0200461 regex = expected_traceback(9, 20, header, min_count=count)
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200462 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200463 else:
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200464 self.assertEqual(trace, '')
Victor Stinner05585cb2011-03-31 13:29:56 +0200465 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200466
Georg Brandldeb92b52012-09-22 08:58:55 +0200467 @unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'),
468 'need faulthandler.dump_traceback_later()')
469 def check_dump_traceback_later(self, repeat=False, cancel=False,
Victor Stinnerde10f402011-04-08 12:57:06 +0200470 file=False, twice=False):
471 if twice:
472 loops = 2
473 else:
474 loops = 1
Victor Stinner024e37a2011-03-31 01:31:06 +0200475 if file:
476 with temporary_filename() as filename:
Georg Brandldeb92b52012-09-22 08:58:55 +0200477 self._check_dump_traceback_later(repeat, cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200478 filename, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200479 else:
Georg Brandldeb92b52012-09-22 08:58:55 +0200480 self._check_dump_traceback_later(repeat, cancel, None, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200481
Georg Brandldeb92b52012-09-22 08:58:55 +0200482 def test_dump_traceback_later(self):
483 self.check_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200484
Georg Brandldeb92b52012-09-22 08:58:55 +0200485 def test_dump_traceback_later_repeat(self):
486 self.check_dump_traceback_later(repeat=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200487
Georg Brandldeb92b52012-09-22 08:58:55 +0200488 def test_dump_traceback_later_cancel(self):
489 self.check_dump_traceback_later(cancel=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200490
Georg Brandldeb92b52012-09-22 08:58:55 +0200491 def test_dump_traceback_later_file(self):
492 self.check_dump_traceback_later(file=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200493
Georg Brandldeb92b52012-09-22 08:58:55 +0200494 def test_dump_traceback_later_twice(self):
495 self.check_dump_traceback_later(twice=True)
Victor Stinnerde10f402011-04-08 12:57:06 +0200496
Victor Stinner024e37a2011-03-31 01:31:06 +0200497 @unittest.skipIf(not hasattr(faulthandler, "register"),
498 "need faulthandler.register")
Victor Stinnera01ca122011-04-01 12:56:17 +0200499 def check_register(self, filename=False, all_threads=False,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200500 unregister=False, chain=False):
Victor Stinner024e37a2011-03-31 01:31:06 +0200501 """
502 Register a handler displaying the traceback on a user signal. Raise the
503 signal and check the written traceback.
504
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200505 If chain is True, check that the previous signal handler is called.
506
Victor Stinner024e37a2011-03-31 01:31:06 +0200507 Raise an error if the output doesn't match the expected format.
508 """
Victor Stinnera01ca122011-04-01 12:56:17 +0200509 signum = signal.SIGUSR1
Victor Stinner024e37a2011-03-31 01:31:06 +0200510 code = """
511import faulthandler
512import os
513import signal
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200514import sys
Victor Stinner024e37a2011-03-31 01:31:06 +0200515
516def func(signum):
517 os.kill(os.getpid(), signum)
518
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200519def handler(signum, frame):
520 handler.called = True
521handler.called = False
522
523exitcode = 0
Victor Stinnera01ca122011-04-01 12:56:17 +0200524signum = {signum}
525unregister = {unregister}
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200526chain = {chain}
527
Victor Stinner024e37a2011-03-31 01:31:06 +0200528if {has_filename}:
529 file = open({filename}, "wb")
530else:
531 file = None
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200532if chain:
533 signal.signal(signum, handler)
534faulthandler.register(signum, file=file,
535 all_threads={all_threads}, chain={chain})
Victor Stinnera01ca122011-04-01 12:56:17 +0200536if unregister:
537 faulthandler.unregister(signum)
Victor Stinner024e37a2011-03-31 01:31:06 +0200538func(signum)
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200539if chain and not handler.called:
540 if file is not None:
541 output = file
542 else:
543 output = sys.stderr
544 print("Error: signal handler not called!", file=output)
545 exitcode = 1
Victor Stinner024e37a2011-03-31 01:31:06 +0200546if file is not None:
547 file.close()
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200548sys.exit(exitcode)
Victor Stinner024e37a2011-03-31 01:31:06 +0200549""".strip()
550 code = code.format(
551 filename=repr(filename),
552 has_filename=bool(filename),
553 all_threads=all_threads,
Victor Stinnera01ca122011-04-01 12:56:17 +0200554 signum=signum,
555 unregister=unregister,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200556 chain=chain,
Victor Stinner024e37a2011-03-31 01:31:06 +0200557 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200558 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200559 trace = '\n'.join(trace)
Victor Stinnera01ca122011-04-01 12:56:17 +0200560 if not unregister:
561 if all_threads:
562 regex = 'Current thread XXX:\n'
563 else:
564 regex = 'Traceback \(most recent call first\):\n'
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200565 regex = expected_traceback(7, 28, regex)
Victor Stinnera01ca122011-04-01 12:56:17 +0200566 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200567 else:
Victor Stinnera01ca122011-04-01 12:56:17 +0200568 self.assertEqual(trace, '')
569 if unregister:
570 self.assertNotEqual(exitcode, 0)
571 else:
572 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200573
574 def test_register(self):
575 self.check_register()
576
Victor Stinnera01ca122011-04-01 12:56:17 +0200577 def test_unregister(self):
578 self.check_register(unregister=True)
579
Victor Stinner024e37a2011-03-31 01:31:06 +0200580 def test_register_file(self):
581 with temporary_filename() as filename:
582 self.check_register(filename=filename)
583
584 def test_register_threads(self):
585 self.check_register(all_threads=True)
586
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200587 def test_register_chain(self):
588 self.check_register(chain=True)
589
Victor Stinner024e37a2011-03-31 01:31:06 +0200590
Victor Stinner024e37a2011-03-31 01:31:06 +0200591if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400592 unittest.main()