blob: d78bcb0a95f8a973f6d649f2fc3ab0b97990a132 [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
Victor Stinner330426c2013-07-03 22:29:42 +0200110 @unittest.skipIf(sys.platform.startswith('aix'),
111 "the first page of memory is a mapped read-only on AIX")
Victor Stinner024e37a2011-03-31 01:31:06 +0200112 def test_read_null(self):
113 self.check_fatal_error("""
114import faulthandler
115faulthandler.enable()
116faulthandler._read_null()
117""".strip(),
118 3,
Victor Stinnera8db3782011-08-08 22:43:45 +0200119 # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion
120 '(?:Segmentation fault|Bus error|Illegal instruction)')
Victor Stinner024e37a2011-03-31 01:31:06 +0200121
122 def test_sigsegv(self):
123 self.check_fatal_error("""
124import faulthandler
125faulthandler.enable()
126faulthandler._sigsegv()
127""".strip(),
128 3,
129 'Segmentation fault')
130
Victor Stinnerd727e232011-04-01 12:13:55 +0200131 def test_sigabrt(self):
132 self.check_fatal_error("""
133import faulthandler
134faulthandler.enable()
135faulthandler._sigabrt()
136""".strip(),
137 3,
138 'Aborted')
139
Victor Stinner024e37a2011-03-31 01:31:06 +0200140 @unittest.skipIf(sys.platform == 'win32',
141 "SIGFPE cannot be caught on Windows")
142 def test_sigfpe(self):
143 self.check_fatal_error("""
144import faulthandler
145faulthandler.enable()
146faulthandler._sigfpe()
147""".strip(),
148 3,
149 'Floating point exception')
150
151 @unittest.skipIf(not hasattr(faulthandler, '_sigbus'),
152 "need faulthandler._sigbus()")
153 def test_sigbus(self):
154 self.check_fatal_error("""
155import faulthandler
156faulthandler.enable()
157faulthandler._sigbus()
158""".strip(),
159 3,
160 'Bus error')
161
162 @unittest.skipIf(not hasattr(faulthandler, '_sigill'),
163 "need faulthandler._sigill()")
164 def test_sigill(self):
165 self.check_fatal_error("""
166import faulthandler
167faulthandler.enable()
168faulthandler._sigill()
169""".strip(),
170 3,
171 'Illegal instruction')
172
173 def test_fatal_error(self):
174 self.check_fatal_error("""
175import faulthandler
176faulthandler._fatal_error(b'xyz')
177""".strip(),
178 2,
179 'xyz')
180
Charles-François Natali3391e642011-09-01 23:08:21 +0200181 @unittest.skipIf(sys.platform.startswith('openbsd') and HAVE_THREADS,
182 "Issue #12868: sigaltstack() doesn't work on "
183 "OpenBSD if Python is compiled with pthread")
Victor Stinner024e37a2011-03-31 01:31:06 +0200184 @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'),
185 'need faulthandler._stack_overflow()')
186 def test_stack_overflow(self):
187 self.check_fatal_error("""
188import faulthandler
189faulthandler.enable()
190faulthandler._stack_overflow()
191""".strip(),
192 3,
Victor Stinnerf0480752011-03-31 11:34:08 +0200193 '(?:Segmentation fault|Bus error)',
194 other_regex='unable to raise a stack overflow')
Victor Stinner024e37a2011-03-31 01:31:06 +0200195
196 def test_gil_released(self):
197 self.check_fatal_error("""
198import faulthandler
199faulthandler.enable()
200faulthandler._read_null(True)
201""".strip(),
202 3,
Victor Stinnera8db3782011-08-08 22:43:45 +0200203 '(?:Segmentation fault|Bus error|Illegal instruction)')
Victor Stinner024e37a2011-03-31 01:31:06 +0200204
205 def test_enable_file(self):
206 with temporary_filename() as filename:
207 self.check_fatal_error("""
208import faulthandler
209output = open({filename}, 'wb')
210faulthandler.enable(output)
Victor Stinner56785392013-06-17 23:37:59 +0200211faulthandler._sigsegv()
Victor Stinner024e37a2011-03-31 01:31:06 +0200212""".strip().format(filename=repr(filename)),
213 4,
Victor Stinner56785392013-06-17 23:37:59 +0200214 'Segmentation fault',
Victor Stinner024e37a2011-03-31 01:31:06 +0200215 filename=filename)
216
Victor Stinner7bba62f2011-05-07 12:43:00 +0200217 def test_enable_single_thread(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200218 self.check_fatal_error("""
219import faulthandler
Victor Stinner7bba62f2011-05-07 12:43:00 +0200220faulthandler.enable(all_threads=False)
Victor Stinner56785392013-06-17 23:37:59 +0200221faulthandler._sigsegv()
Victor Stinner024e37a2011-03-31 01:31:06 +0200222""".strip(),
223 3,
Victor Stinner56785392013-06-17 23:37:59 +0200224 'Segmentation fault',
Victor Stinner7bba62f2011-05-07 12:43:00 +0200225 all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200226
227 def test_disable(self):
228 code = """
229import faulthandler
230faulthandler.enable()
231faulthandler.disable()
Victor Stinner56785392013-06-17 23:37:59 +0200232faulthandler._sigsegv()
Victor Stinner024e37a2011-03-31 01:31:06 +0200233""".strip()
234 not_expected = 'Fatal Python error'
Ezio Melotti25a40452013-03-05 20:26:17 +0200235 with support.suppress_crash_popup():
236 stderr, exitcode = self.get_output(code)
Victor Stinner024e37a2011-03-31 01:31:06 +0200237 stder = '\n'.join(stderr)
238 self.assertTrue(not_expected not in stderr,
239 "%r is present in %r" % (not_expected, stderr))
Victor Stinner05585cb2011-03-31 13:29:56 +0200240 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200241
242 def test_is_enabled(self):
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200243 orig_stderr = sys.stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200244 try:
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200245 # regrtest may replace sys.stderr by io.StringIO object, but
246 # faulthandler.enable() requires that sys.stderr has a fileno()
247 # method
Victor Stinner72488502011-06-29 23:24:31 +0200248 sys.stderr = sys.__stderr__
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200249
250 was_enabled = faulthandler.is_enabled()
251 try:
Victor Stinner024e37a2011-03-31 01:31:06 +0200252 faulthandler.enable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200253 self.assertTrue(faulthandler.is_enabled())
Victor Stinner024e37a2011-03-31 01:31:06 +0200254 faulthandler.disable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200255 self.assertFalse(faulthandler.is_enabled())
256 finally:
257 if was_enabled:
258 faulthandler.enable()
259 else:
260 faulthandler.disable()
261 finally:
262 sys.stderr = orig_stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200263
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200264 def test_disabled_by_default(self):
265 # By default, the module should be disabled
266 code = "import faulthandler; print(faulthandler.is_enabled())"
Victor Stinneref8115e2013-06-25 21:54:17 +0200267 args = (sys.executable, '-E', '-c', code)
Victor Stinner88983502013-09-08 11:36:23 +0200268 # don't use assert_python_ok() because it always enable faulthandler
269 output = subprocess.check_output(args)
270 self.assertEqual(output.rstrip(), b"False")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200271
272 def test_sys_xoptions(self):
273 # Test python -X faulthandler
274 code = "import faulthandler; print(faulthandler.is_enabled())"
Victor Stinner88983502013-09-08 11:36:23 +0200275 args = (sys.executable, "-E", "-X", "faulthandler", "-c", code)
276 # don't use assert_python_ok() because it always enable faulthandler
277 output = subprocess.check_output(args)
278 self.assertEqual(output.rstrip(), b"True")
279
280 def test_env_var(self):
281 # empty env var
282 code = "import faulthandler; print(faulthandler.is_enabled())"
283 args = (sys.executable, "-c", code)
284 env = os.environ.copy()
285 env['PYTHONFAULTHANDLER'] = ''
286 # don't use assert_python_ok() because it always enable faulthandler
287 output = subprocess.check_output(args, env=env)
288 self.assertEqual(output.rstrip(), b"False")
289
290 # non-empty env var
291 env = os.environ.copy()
292 env['PYTHONFAULTHANDLER'] = '1'
293 output = subprocess.check_output(args, env=env)
294 self.assertEqual(output.rstrip(), b"True")
Victor Stinnerd5698cb2012-07-31 02:55:49 +0200295
Victor Stinner024e37a2011-03-31 01:31:06 +0200296 def check_dump_traceback(self, filename):
297 """
298 Explicitly call dump_traceback() function and check its output.
299 Raise an error if the output doesn't match the expected format.
300 """
301 code = """
302import faulthandler
303
304def funcB():
305 if {has_filename}:
306 with open({filename}, "wb") as fp:
Victor Stinner7bba62f2011-05-07 12:43:00 +0200307 faulthandler.dump_traceback(fp, all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200308 else:
Victor Stinner7bba62f2011-05-07 12:43:00 +0200309 faulthandler.dump_traceback(all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200310
311def funcA():
312 funcB()
313
314funcA()
315""".strip()
316 code = code.format(
317 filename=repr(filename),
318 has_filename=bool(filename),
319 )
320 if filename:
321 lineno = 6
322 else:
323 lineno = 8
324 expected = [
325 'Traceback (most recent call first):',
326 ' File "<string>", line %s in funcB' % lineno,
327 ' File "<string>", line 11 in funcA',
328 ' File "<string>", line 13 in <module>'
329 ]
Victor Stinner05585cb2011-03-31 13:29:56 +0200330 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200331 self.assertEqual(trace, expected)
Victor Stinner05585cb2011-03-31 13:29:56 +0200332 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200333
334 def test_dump_traceback(self):
335 self.check_dump_traceback(None)
Victor Stinner19402332011-03-31 18:15:52 +0200336
337 def test_dump_traceback_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200338 with temporary_filename() as filename:
339 self.check_dump_traceback(filename)
340
Victor Stinner53386d82012-08-01 19:45:34 +0200341 def test_truncate(self):
342 maxlen = 500
343 func_name = 'x' * (maxlen + 50)
344 truncated = 'x' * maxlen + '...'
345 code = """
346import faulthandler
347
348def {func_name}():
349 faulthandler.dump_traceback(all_threads=False)
350
351{func_name}()
352""".strip()
353 code = code.format(
354 func_name=func_name,
355 )
356 expected = [
357 'Traceback (most recent call first):',
358 ' File "<string>", line 4 in %s' % truncated,
359 ' File "<string>", line 6 in <module>'
360 ]
361 trace, exitcode = self.get_output(code)
362 self.assertEqual(trace, expected)
363 self.assertEqual(exitcode, 0)
364
Victor Stinnerff4cd882011-04-07 11:50:25 +0200365 @unittest.skipIf(not HAVE_THREADS, 'need threads')
Victor Stinner024e37a2011-03-31 01:31:06 +0200366 def check_dump_traceback_threads(self, filename):
367 """
368 Call explicitly dump_traceback(all_threads=True) and check the output.
369 Raise an error if the output doesn't match the expected format.
370 """
371 code = """
372import faulthandler
373from threading import Thread, Event
374import time
375
376def dump():
377 if {filename}:
378 with open({filename}, "wb") as fp:
379 faulthandler.dump_traceback(fp, all_threads=True)
380 else:
381 faulthandler.dump_traceback(all_threads=True)
382
383class Waiter(Thread):
384 # avoid blocking if the main thread raises an exception.
385 daemon = True
386
387 def __init__(self):
388 Thread.__init__(self)
389 self.running = Event()
390 self.stop = Event()
391
392 def run(self):
393 self.running.set()
394 self.stop.wait()
395
396waiter = Waiter()
397waiter.start()
398waiter.running.wait()
399dump()
400waiter.stop.set()
401waiter.join()
402""".strip()
403 code = code.format(filename=repr(filename))
Victor Stinner05585cb2011-03-31 13:29:56 +0200404 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200405 output = '\n'.join(output)
406 if filename:
407 lineno = 8
408 else:
409 lineno = 10
410 regex = """
411^Thread 0x[0-9a-f]+:
Victor Stinner1b3241f2011-04-03 18:41:22 +0200412(?: File ".*threading.py", line [0-9]+ in [_a-z]+
413){{1,3}} File "<string>", line 23 in run
Victor Stinner024e37a2011-03-31 01:31:06 +0200414 File ".*threading.py", line [0-9]+ in _bootstrap_inner
415 File ".*threading.py", line [0-9]+ in _bootstrap
416
417Current thread XXX:
418 File "<string>", line {lineno} in dump
419 File "<string>", line 28 in <module>$
420""".strip()
421 regex = regex.format(lineno=lineno)
422 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200423 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200424
425 def test_dump_traceback_threads(self):
426 self.check_dump_traceback_threads(None)
Victor Stinner19402332011-03-31 18:15:52 +0200427
428 def test_dump_traceback_threads_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200429 with temporary_filename() as filename:
430 self.check_dump_traceback_threads(filename)
431
Georg Brandldeb92b52012-09-22 08:58:55 +0200432 def _check_dump_traceback_later(self, repeat, cancel, filename, loops):
Victor Stinner024e37a2011-03-31 01:31:06 +0200433 """
434 Check how many times the traceback is written in timeout x 2.5 seconds,
435 or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending
436 on repeat and cancel options.
437
438 Raise an error if the output doesn't match the expect format.
439 """
Victor Stinnerc790a532011-04-08 13:39:59 +0200440 timeout_str = str(datetime.timedelta(seconds=TIMEOUT))
Victor Stinner024e37a2011-03-31 01:31:06 +0200441 code = """
442import faulthandler
443import time
444
Victor Stinnerde10f402011-04-08 12:57:06 +0200445def func(timeout, repeat, cancel, file, loops):
446 for loop in range(loops):
Georg Brandldeb92b52012-09-22 08:58:55 +0200447 faulthandler.dump_traceback_later(timeout, repeat=repeat, file=file)
Victor Stinnerde10f402011-04-08 12:57:06 +0200448 if cancel:
Georg Brandldeb92b52012-09-22 08:58:55 +0200449 faulthandler.cancel_dump_traceback_later()
Victor Stinner301f3f02011-06-01 13:49:12 +0200450 time.sleep(timeout * 5)
Georg Brandldeb92b52012-09-22 08:58:55 +0200451 faulthandler.cancel_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200452
Victor Stinner44378d42011-04-01 15:37:12 +0200453timeout = {timeout}
Victor Stinner024e37a2011-03-31 01:31:06 +0200454repeat = {repeat}
455cancel = {cancel}
Victor Stinnerde10f402011-04-08 12:57:06 +0200456loops = {loops}
Victor Stinner024e37a2011-03-31 01:31:06 +0200457if {has_filename}:
458 file = open({filename}, "wb")
459else:
460 file = None
Victor Stinnerde10f402011-04-08 12:57:06 +0200461func(timeout, repeat, cancel, file, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200462if file is not None:
463 file.close()
464""".strip()
465 code = code.format(
Victor Stinnerde10f402011-04-08 12:57:06 +0200466 timeout=TIMEOUT,
Victor Stinner024e37a2011-03-31 01:31:06 +0200467 repeat=repeat,
468 cancel=cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200469 loops=loops,
470 has_filename=bool(filename),
471 filename=repr(filename),
Victor Stinner024e37a2011-03-31 01:31:06 +0200472 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200473 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200474 trace = '\n'.join(trace)
475
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200476 if not cancel:
Victor Stinnerde10f402011-04-08 12:57:06 +0200477 count = loops
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200478 if repeat:
Victor Stinnerde10f402011-04-08 12:57:06 +0200479 count *= 2
Victor Stinnerc790a532011-04-08 13:39:59 +0200480 header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+:\n' % timeout_str
Victor Stinner301f3f02011-06-01 13:49:12 +0200481 regex = expected_traceback(9, 20, header, min_count=count)
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200482 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200483 else:
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200484 self.assertEqual(trace, '')
Victor Stinner05585cb2011-03-31 13:29:56 +0200485 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200486
Georg Brandldeb92b52012-09-22 08:58:55 +0200487 @unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'),
488 'need faulthandler.dump_traceback_later()')
489 def check_dump_traceback_later(self, repeat=False, cancel=False,
Victor Stinnerde10f402011-04-08 12:57:06 +0200490 file=False, twice=False):
491 if twice:
492 loops = 2
493 else:
494 loops = 1
Victor Stinner024e37a2011-03-31 01:31:06 +0200495 if file:
496 with temporary_filename() as filename:
Georg Brandldeb92b52012-09-22 08:58:55 +0200497 self._check_dump_traceback_later(repeat, cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200498 filename, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200499 else:
Georg Brandldeb92b52012-09-22 08:58:55 +0200500 self._check_dump_traceback_later(repeat, cancel, None, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200501
Georg Brandldeb92b52012-09-22 08:58:55 +0200502 def test_dump_traceback_later(self):
503 self.check_dump_traceback_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200504
Georg Brandldeb92b52012-09-22 08:58:55 +0200505 def test_dump_traceback_later_repeat(self):
506 self.check_dump_traceback_later(repeat=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200507
Georg Brandldeb92b52012-09-22 08:58:55 +0200508 def test_dump_traceback_later_cancel(self):
509 self.check_dump_traceback_later(cancel=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200510
Georg Brandldeb92b52012-09-22 08:58:55 +0200511 def test_dump_traceback_later_file(self):
512 self.check_dump_traceback_later(file=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200513
Georg Brandldeb92b52012-09-22 08:58:55 +0200514 def test_dump_traceback_later_twice(self):
515 self.check_dump_traceback_later(twice=True)
Victor Stinnerde10f402011-04-08 12:57:06 +0200516
Victor Stinner024e37a2011-03-31 01:31:06 +0200517 @unittest.skipIf(not hasattr(faulthandler, "register"),
518 "need faulthandler.register")
Victor Stinnera01ca122011-04-01 12:56:17 +0200519 def check_register(self, filename=False, all_threads=False,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200520 unregister=False, chain=False):
Victor Stinner024e37a2011-03-31 01:31:06 +0200521 """
522 Register a handler displaying the traceback on a user signal. Raise the
523 signal and check the written traceback.
524
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200525 If chain is True, check that the previous signal handler is called.
526
Victor Stinner024e37a2011-03-31 01:31:06 +0200527 Raise an error if the output doesn't match the expected format.
528 """
Victor Stinnera01ca122011-04-01 12:56:17 +0200529 signum = signal.SIGUSR1
Victor Stinner024e37a2011-03-31 01:31:06 +0200530 code = """
531import faulthandler
532import os
533import signal
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200534import sys
Victor Stinner024e37a2011-03-31 01:31:06 +0200535
536def func(signum):
537 os.kill(os.getpid(), signum)
538
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200539def handler(signum, frame):
540 handler.called = True
541handler.called = False
542
543exitcode = 0
Victor Stinnera01ca122011-04-01 12:56:17 +0200544signum = {signum}
545unregister = {unregister}
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200546chain = {chain}
547
Victor Stinner024e37a2011-03-31 01:31:06 +0200548if {has_filename}:
549 file = open({filename}, "wb")
550else:
551 file = None
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200552if chain:
553 signal.signal(signum, handler)
554faulthandler.register(signum, file=file,
555 all_threads={all_threads}, chain={chain})
Victor Stinnera01ca122011-04-01 12:56:17 +0200556if unregister:
557 faulthandler.unregister(signum)
Victor Stinner024e37a2011-03-31 01:31:06 +0200558func(signum)
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200559if chain and not handler.called:
560 if file is not None:
561 output = file
562 else:
563 output = sys.stderr
564 print("Error: signal handler not called!", file=output)
565 exitcode = 1
Victor Stinner024e37a2011-03-31 01:31:06 +0200566if file is not None:
567 file.close()
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200568sys.exit(exitcode)
Victor Stinner024e37a2011-03-31 01:31:06 +0200569""".strip()
570 code = code.format(
571 filename=repr(filename),
572 has_filename=bool(filename),
573 all_threads=all_threads,
Victor Stinnera01ca122011-04-01 12:56:17 +0200574 signum=signum,
575 unregister=unregister,
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200576 chain=chain,
Victor Stinner024e37a2011-03-31 01:31:06 +0200577 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200578 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200579 trace = '\n'.join(trace)
Victor Stinnera01ca122011-04-01 12:56:17 +0200580 if not unregister:
581 if all_threads:
582 regex = 'Current thread XXX:\n'
583 else:
584 regex = 'Traceback \(most recent call first\):\n'
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200585 regex = expected_traceback(7, 28, regex)
Victor Stinnera01ca122011-04-01 12:56:17 +0200586 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200587 else:
Victor Stinnera01ca122011-04-01 12:56:17 +0200588 self.assertEqual(trace, '')
589 if unregister:
590 self.assertNotEqual(exitcode, 0)
591 else:
592 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200593
594 def test_register(self):
595 self.check_register()
596
Victor Stinnera01ca122011-04-01 12:56:17 +0200597 def test_unregister(self):
598 self.check_register(unregister=True)
599
Victor Stinner024e37a2011-03-31 01:31:06 +0200600 def test_register_file(self):
601 with temporary_filename() as filename:
602 self.check_register(filename=filename)
603
604 def test_register_threads(self):
605 self.check_register(all_threads=True)
606
Victor Stinnera9a9dab2011-07-13 23:39:53 +0200607 def test_register_chain(self):
608 self.check_register(chain=True)
609
Victor Stinner024e37a2011-03-31 01:31:06 +0200610
Victor Stinner024e37a2011-03-31 01:31:06 +0200611if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400612 unittest.main()