blob: 2a254af224c02f76410c8f422266c198723534c2 [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
10import tempfile
11import unittest
12
Victor Stinnerff4cd882011-04-07 11:50:25 +020013try:
14 import threading
15 HAVE_THREADS = True
16except ImportError:
17 HAVE_THREADS = False
18
Victor Stinner44378d42011-04-01 15:37:12 +020019TIMEOUT = 0.5
20
Victor Stinner024e37a2011-03-31 01:31:06 +020021try:
22 from resource import setrlimit, RLIMIT_CORE, error as resource_error
23except ImportError:
24 prepare_subprocess = None
25else:
26 def prepare_subprocess():
27 # don't create core file
28 try:
29 setrlimit(RLIMIT_CORE, (0, 0))
30 except (ValueError, resource_error):
31 pass
32
Victor Stinner301f3f02011-06-01 13:49:12 +020033def expected_traceback(lineno1, lineno2, header, min_count=1):
Victor Stinner024e37a2011-03-31 01:31:06 +020034 regex = header
Victor Stinner7ad24e92011-03-31 22:35:49 +020035 regex += ' File "<string>", line %s in func\n' % lineno1
36 regex += ' File "<string>", line %s in <module>' % lineno2
Victor Stinner301f3f02011-06-01 13:49:12 +020037 if 1 < min_count:
38 return '^' + (regex + '\n') * (min_count - 1) + regex
39 else:
40 return '^' + regex + '$'
Victor Stinner024e37a2011-03-31 01:31:06 +020041
42@contextmanager
43def temporary_filename():
44 filename = tempfile.mktemp()
45 try:
46 yield filename
47 finally:
48 support.unlink(filename)
49
50class FaultHandlerTests(unittest.TestCase):
Victor Stinner05585cb2011-03-31 13:29:56 +020051 def get_output(self, code, filename=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020052 """
53 Run the specified code in Python (in a new child process) and read the
54 output from the standard error or from a file (if filename is set).
55 Return the output lines as a list.
56
57 Strip the reference count from the standard error for Python debug
58 build, and replace "Current thread 0x00007f8d8fbd9700" by "Current
59 thread XXX".
60 """
61 options = {}
62 if prepare_subprocess:
63 options['preexec_fn'] = prepare_subprocess
64 process = script_helper.spawn_python('-c', code, **options)
65 stdout, stderr = process.communicate()
66 exitcode = process.wait()
Victor Stinner19402332011-03-31 18:15:52 +020067 output = support.strip_python_stderr(stdout)
68 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020069 if filename:
Victor Stinner19402332011-03-31 18:15:52 +020070 self.assertEqual(output, '')
Victor Stinner024e37a2011-03-31 01:31:06 +020071 with open(filename, "rb") as fp:
72 output = fp.read()
Victor Stinner19402332011-03-31 18:15:52 +020073 output = output.decode('ascii', 'backslashreplace')
Victor Stinner024e37a2011-03-31 01:31:06 +020074 output = re.sub('Current thread 0x[0-9a-f]+',
75 'Current thread XXX',
76 output)
Victor Stinner05585cb2011-03-31 13:29:56 +020077 return output.splitlines(), exitcode
Victor Stinner024e37a2011-03-31 01:31:06 +020078
79 def check_fatal_error(self, code, line_number, name_regex,
Victor Stinner7bba62f2011-05-07 12:43:00 +020080 filename=None, all_threads=True, other_regex=None):
Victor Stinner024e37a2011-03-31 01:31:06 +020081 """
82 Check that the fault handler for fatal errors is enabled and check the
83 traceback from the child process output.
84
85 Raise an error if the output doesn't match the expected format.
86 """
87 if all_threads:
88 header = 'Current thread XXX'
89 else:
90 header = 'Traceback (most recent call first)'
91 regex = """
92^Fatal Python error: {name}
93
94{header}:
95 File "<string>", line {lineno} in <module>$
96""".strip()
97 regex = regex.format(
98 lineno=line_number,
99 name=name_regex,
100 header=re.escape(header))
Victor Stinnerf0480752011-03-31 11:34:08 +0200101 if other_regex:
102 regex += '|' + other_regex
Victor Stinner05585cb2011-03-31 13:29:56 +0200103 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200104 output = '\n'.join(output)
105 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200106 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200107
108 def test_read_null(self):
109 self.check_fatal_error("""
110import faulthandler
111faulthandler.enable()
112faulthandler._read_null()
113""".strip(),
114 3,
115 '(?:Segmentation fault|Bus error)')
116
117 def test_sigsegv(self):
118 self.check_fatal_error("""
119import faulthandler
120faulthandler.enable()
121faulthandler._sigsegv()
122""".strip(),
123 3,
124 'Segmentation fault')
125
Victor Stinnerd727e232011-04-01 12:13:55 +0200126 def test_sigabrt(self):
127 self.check_fatal_error("""
128import faulthandler
129faulthandler.enable()
130faulthandler._sigabrt()
131""".strip(),
132 3,
133 'Aborted')
134
Victor Stinner024e37a2011-03-31 01:31:06 +0200135 @unittest.skipIf(sys.platform == 'win32',
136 "SIGFPE cannot be caught on Windows")
137 def test_sigfpe(self):
138 self.check_fatal_error("""
139import faulthandler
140faulthandler.enable()
141faulthandler._sigfpe()
142""".strip(),
143 3,
144 'Floating point exception')
145
146 @unittest.skipIf(not hasattr(faulthandler, '_sigbus'),
147 "need faulthandler._sigbus()")
148 def test_sigbus(self):
149 self.check_fatal_error("""
150import faulthandler
151faulthandler.enable()
152faulthandler._sigbus()
153""".strip(),
154 3,
155 'Bus error')
156
157 @unittest.skipIf(not hasattr(faulthandler, '_sigill'),
158 "need faulthandler._sigill()")
159 def test_sigill(self):
160 self.check_fatal_error("""
161import faulthandler
162faulthandler.enable()
163faulthandler._sigill()
164""".strip(),
165 3,
166 'Illegal instruction')
167
168 def test_fatal_error(self):
169 self.check_fatal_error("""
170import faulthandler
171faulthandler._fatal_error(b'xyz')
172""".strip(),
173 2,
174 'xyz')
175
Victor Stinner024e37a2011-03-31 01:31:06 +0200176 @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'),
177 'need faulthandler._stack_overflow()')
178 def test_stack_overflow(self):
179 self.check_fatal_error("""
180import faulthandler
181faulthandler.enable()
182faulthandler._stack_overflow()
183""".strip(),
184 3,
Victor Stinnerf0480752011-03-31 11:34:08 +0200185 '(?:Segmentation fault|Bus error)',
186 other_regex='unable to raise a stack overflow')
Victor Stinner024e37a2011-03-31 01:31:06 +0200187
188 def test_gil_released(self):
189 self.check_fatal_error("""
190import faulthandler
191faulthandler.enable()
192faulthandler._read_null(True)
193""".strip(),
194 3,
195 '(?:Segmentation fault|Bus error)')
196
197 def test_enable_file(self):
198 with temporary_filename() as filename:
199 self.check_fatal_error("""
200import faulthandler
201output = open({filename}, 'wb')
202faulthandler.enable(output)
Victor Stinner44378d42011-04-01 15:37:12 +0200203faulthandler._read_null()
Victor Stinner024e37a2011-03-31 01:31:06 +0200204""".strip().format(filename=repr(filename)),
205 4,
206 '(?:Segmentation fault|Bus error)',
207 filename=filename)
208
Victor Stinner7bba62f2011-05-07 12:43:00 +0200209 def test_enable_single_thread(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200210 self.check_fatal_error("""
211import faulthandler
Victor Stinner7bba62f2011-05-07 12:43:00 +0200212faulthandler.enable(all_threads=False)
Victor Stinner44378d42011-04-01 15:37:12 +0200213faulthandler._read_null()
Victor Stinner024e37a2011-03-31 01:31:06 +0200214""".strip(),
215 3,
216 '(?:Segmentation fault|Bus error)',
Victor Stinner7bba62f2011-05-07 12:43:00 +0200217 all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200218
219 def test_disable(self):
220 code = """
221import faulthandler
222faulthandler.enable()
223faulthandler.disable()
224faulthandler._read_null()
225""".strip()
226 not_expected = 'Fatal Python error'
Victor Stinner05585cb2011-03-31 13:29:56 +0200227 stderr, exitcode = self.get_output(code)
Victor Stinner024e37a2011-03-31 01:31:06 +0200228 stder = '\n'.join(stderr)
229 self.assertTrue(not_expected not in stderr,
230 "%r is present in %r" % (not_expected, stderr))
Victor Stinner05585cb2011-03-31 13:29:56 +0200231 self.assertNotEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200232
233 def test_is_enabled(self):
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200234 orig_stderr = sys.stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200235 try:
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200236 # regrtest may replace sys.stderr by io.StringIO object, but
237 # faulthandler.enable() requires that sys.stderr has a fileno()
238 # method
Victor Stinner72488502011-06-29 23:24:31 +0200239 sys.stderr = sys.__stderr__
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200240
241 was_enabled = faulthandler.is_enabled()
242 try:
Victor Stinner024e37a2011-03-31 01:31:06 +0200243 faulthandler.enable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200244 self.assertTrue(faulthandler.is_enabled())
Victor Stinner024e37a2011-03-31 01:31:06 +0200245 faulthandler.disable()
Victor Stinner7e32f3a2011-06-29 13:44:05 +0200246 self.assertFalse(faulthandler.is_enabled())
247 finally:
248 if was_enabled:
249 faulthandler.enable()
250 else:
251 faulthandler.disable()
252 finally:
253 sys.stderr = orig_stderr
Victor Stinner024e37a2011-03-31 01:31:06 +0200254
255 def check_dump_traceback(self, filename):
256 """
257 Explicitly call dump_traceback() function and check its output.
258 Raise an error if the output doesn't match the expected format.
259 """
260 code = """
261import faulthandler
262
263def funcB():
264 if {has_filename}:
265 with open({filename}, "wb") as fp:
Victor Stinner7bba62f2011-05-07 12:43:00 +0200266 faulthandler.dump_traceback(fp, all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200267 else:
Victor Stinner7bba62f2011-05-07 12:43:00 +0200268 faulthandler.dump_traceback(all_threads=False)
Victor Stinner024e37a2011-03-31 01:31:06 +0200269
270def funcA():
271 funcB()
272
273funcA()
274""".strip()
275 code = code.format(
276 filename=repr(filename),
277 has_filename=bool(filename),
278 )
279 if filename:
280 lineno = 6
281 else:
282 lineno = 8
283 expected = [
284 'Traceback (most recent call first):',
285 ' File "<string>", line %s in funcB' % lineno,
286 ' File "<string>", line 11 in funcA',
287 ' File "<string>", line 13 in <module>'
288 ]
Victor Stinner05585cb2011-03-31 13:29:56 +0200289 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200290 self.assertEqual(trace, expected)
Victor Stinner05585cb2011-03-31 13:29:56 +0200291 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200292
293 def test_dump_traceback(self):
294 self.check_dump_traceback(None)
Victor Stinner19402332011-03-31 18:15:52 +0200295
296 def test_dump_traceback_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200297 with temporary_filename() as filename:
298 self.check_dump_traceback(filename)
299
Victor Stinnerff4cd882011-04-07 11:50:25 +0200300 @unittest.skipIf(not HAVE_THREADS, 'need threads')
Victor Stinner024e37a2011-03-31 01:31:06 +0200301 def check_dump_traceback_threads(self, filename):
302 """
303 Call explicitly dump_traceback(all_threads=True) and check the output.
304 Raise an error if the output doesn't match the expected format.
305 """
306 code = """
307import faulthandler
308from threading import Thread, Event
309import time
310
311def dump():
312 if {filename}:
313 with open({filename}, "wb") as fp:
314 faulthandler.dump_traceback(fp, all_threads=True)
315 else:
316 faulthandler.dump_traceback(all_threads=True)
317
318class Waiter(Thread):
319 # avoid blocking if the main thread raises an exception.
320 daemon = True
321
322 def __init__(self):
323 Thread.__init__(self)
324 self.running = Event()
325 self.stop = Event()
326
327 def run(self):
328 self.running.set()
329 self.stop.wait()
330
331waiter = Waiter()
332waiter.start()
333waiter.running.wait()
334dump()
335waiter.stop.set()
336waiter.join()
337""".strip()
338 code = code.format(filename=repr(filename))
Victor Stinner05585cb2011-03-31 13:29:56 +0200339 output, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200340 output = '\n'.join(output)
341 if filename:
342 lineno = 8
343 else:
344 lineno = 10
345 regex = """
346^Thread 0x[0-9a-f]+:
Victor Stinner1b3241f2011-04-03 18:41:22 +0200347(?: File ".*threading.py", line [0-9]+ in [_a-z]+
348){{1,3}} File "<string>", line 23 in run
Victor Stinner024e37a2011-03-31 01:31:06 +0200349 File ".*threading.py", line [0-9]+ in _bootstrap_inner
350 File ".*threading.py", line [0-9]+ in _bootstrap
351
352Current thread XXX:
353 File "<string>", line {lineno} in dump
354 File "<string>", line 28 in <module>$
355""".strip()
356 regex = regex.format(lineno=lineno)
357 self.assertRegex(output, regex)
Victor Stinner05585cb2011-03-31 13:29:56 +0200358 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200359
360 def test_dump_traceback_threads(self):
361 self.check_dump_traceback_threads(None)
Victor Stinner19402332011-03-31 18:15:52 +0200362
363 def test_dump_traceback_threads_file(self):
Victor Stinner024e37a2011-03-31 01:31:06 +0200364 with temporary_filename() as filename:
365 self.check_dump_traceback_threads(filename)
366
Victor Stinnerde10f402011-04-08 12:57:06 +0200367 def _check_dump_tracebacks_later(self, repeat, cancel, filename, loops):
Victor Stinner024e37a2011-03-31 01:31:06 +0200368 """
369 Check how many times the traceback is written in timeout x 2.5 seconds,
370 or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending
371 on repeat and cancel options.
372
373 Raise an error if the output doesn't match the expect format.
374 """
Victor Stinnerc790a532011-04-08 13:39:59 +0200375 timeout_str = str(datetime.timedelta(seconds=TIMEOUT))
Victor Stinner024e37a2011-03-31 01:31:06 +0200376 code = """
377import faulthandler
378import time
379
Victor Stinnerde10f402011-04-08 12:57:06 +0200380def func(timeout, repeat, cancel, file, loops):
381 for loop in range(loops):
382 faulthandler.dump_tracebacks_later(timeout, repeat=repeat, file=file)
383 if cancel:
384 faulthandler.cancel_dump_tracebacks_later()
Victor Stinner301f3f02011-06-01 13:49:12 +0200385 time.sleep(timeout * 5)
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200386 faulthandler.cancel_dump_tracebacks_later()
Victor Stinner024e37a2011-03-31 01:31:06 +0200387
Victor Stinner44378d42011-04-01 15:37:12 +0200388timeout = {timeout}
Victor Stinner024e37a2011-03-31 01:31:06 +0200389repeat = {repeat}
390cancel = {cancel}
Victor Stinnerde10f402011-04-08 12:57:06 +0200391loops = {loops}
Victor Stinner024e37a2011-03-31 01:31:06 +0200392if {has_filename}:
393 file = open({filename}, "wb")
394else:
395 file = None
Victor Stinnerde10f402011-04-08 12:57:06 +0200396func(timeout, repeat, cancel, file, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200397if file is not None:
398 file.close()
399""".strip()
400 code = code.format(
Victor Stinnerde10f402011-04-08 12:57:06 +0200401 timeout=TIMEOUT,
Victor Stinner024e37a2011-03-31 01:31:06 +0200402 repeat=repeat,
403 cancel=cancel,
Victor Stinnerde10f402011-04-08 12:57:06 +0200404 loops=loops,
405 has_filename=bool(filename),
406 filename=repr(filename),
Victor Stinner024e37a2011-03-31 01:31:06 +0200407 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200408 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200409 trace = '\n'.join(trace)
410
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200411 if not cancel:
Victor Stinnerde10f402011-04-08 12:57:06 +0200412 count = loops
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200413 if repeat:
Victor Stinnerde10f402011-04-08 12:57:06 +0200414 count *= 2
Victor Stinnerc790a532011-04-08 13:39:59 +0200415 header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+:\n' % timeout_str
Victor Stinner301f3f02011-06-01 13:49:12 +0200416 regex = expected_traceback(9, 20, header, min_count=count)
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200417 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200418 else:
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200419 self.assertEqual(trace, '')
Victor Stinner05585cb2011-03-31 13:29:56 +0200420 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200421
422 @unittest.skipIf(not hasattr(faulthandler, 'dump_tracebacks_later'),
423 'need faulthandler.dump_tracebacks_later()')
424 def check_dump_tracebacks_later(self, repeat=False, cancel=False,
Victor Stinnerde10f402011-04-08 12:57:06 +0200425 file=False, twice=False):
426 if twice:
427 loops = 2
428 else:
429 loops = 1
Victor Stinner024e37a2011-03-31 01:31:06 +0200430 if file:
431 with temporary_filename() as filename:
Victor Stinnerde10f402011-04-08 12:57:06 +0200432 self._check_dump_tracebacks_later(repeat, cancel,
433 filename, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200434 else:
Victor Stinnerde10f402011-04-08 12:57:06 +0200435 self._check_dump_tracebacks_later(repeat, cancel, None, loops)
Victor Stinner024e37a2011-03-31 01:31:06 +0200436
437 def test_dump_tracebacks_later(self):
438 self.check_dump_tracebacks_later()
439
440 def test_dump_tracebacks_later_repeat(self):
441 self.check_dump_tracebacks_later(repeat=True)
442
Victor Stinnerf77ccc62011-04-03 18:45:42 +0200443 def test_dump_tracebacks_later_cancel(self):
444 self.check_dump_tracebacks_later(cancel=True)
Victor Stinner024e37a2011-03-31 01:31:06 +0200445
446 def test_dump_tracebacks_later_file(self):
447 self.check_dump_tracebacks_later(file=True)
448
Victor Stinnerde10f402011-04-08 12:57:06 +0200449 def test_dump_tracebacks_later_twice(self):
450 self.check_dump_tracebacks_later(twice=True)
451
Victor Stinner024e37a2011-03-31 01:31:06 +0200452 @unittest.skipIf(not hasattr(faulthandler, "register"),
453 "need faulthandler.register")
Victor Stinnera01ca122011-04-01 12:56:17 +0200454 def check_register(self, filename=False, all_threads=False,
455 unregister=False):
Victor Stinner024e37a2011-03-31 01:31:06 +0200456 """
457 Register a handler displaying the traceback on a user signal. Raise the
458 signal and check the written traceback.
459
460 Raise an error if the output doesn't match the expected format.
461 """
Victor Stinnera01ca122011-04-01 12:56:17 +0200462 signum = signal.SIGUSR1
Victor Stinner024e37a2011-03-31 01:31:06 +0200463 code = """
464import faulthandler
465import os
466import signal
467
468def func(signum):
469 os.kill(os.getpid(), signum)
470
Victor Stinnera01ca122011-04-01 12:56:17 +0200471signum = {signum}
472unregister = {unregister}
Victor Stinner024e37a2011-03-31 01:31:06 +0200473if {has_filename}:
474 file = open({filename}, "wb")
475else:
476 file = None
477faulthandler.register(signum, file=file, all_threads={all_threads})
Victor Stinnera01ca122011-04-01 12:56:17 +0200478if unregister:
479 faulthandler.unregister(signum)
Victor Stinner024e37a2011-03-31 01:31:06 +0200480func(signum)
481if file is not None:
482 file.close()
483""".strip()
484 code = code.format(
485 filename=repr(filename),
486 has_filename=bool(filename),
487 all_threads=all_threads,
Victor Stinnera01ca122011-04-01 12:56:17 +0200488 signum=signum,
489 unregister=unregister,
Victor Stinner024e37a2011-03-31 01:31:06 +0200490 )
Victor Stinner05585cb2011-03-31 13:29:56 +0200491 trace, exitcode = self.get_output(code, filename)
Victor Stinner024e37a2011-03-31 01:31:06 +0200492 trace = '\n'.join(trace)
Victor Stinnera01ca122011-04-01 12:56:17 +0200493 if not unregister:
494 if all_threads:
495 regex = 'Current thread XXX:\n'
496 else:
497 regex = 'Traceback \(most recent call first\):\n'
498 regex = expected_traceback(6, 17, regex)
499 self.assertRegex(trace, regex)
Victor Stinner024e37a2011-03-31 01:31:06 +0200500 else:
Victor Stinnera01ca122011-04-01 12:56:17 +0200501 self.assertEqual(trace, '')
502 if unregister:
503 self.assertNotEqual(exitcode, 0)
504 else:
505 self.assertEqual(exitcode, 0)
Victor Stinner024e37a2011-03-31 01:31:06 +0200506
507 def test_register(self):
508 self.check_register()
509
Victor Stinnera01ca122011-04-01 12:56:17 +0200510 def test_unregister(self):
511 self.check_register(unregister=True)
512
Victor Stinner024e37a2011-03-31 01:31:06 +0200513 def test_register_file(self):
514 with temporary_filename() as filename:
515 self.check_register(filename=filename)
516
517 def test_register_threads(self):
518 self.check_register(all_threads=True)
519
520
521def test_main():
522 support.run_unittest(FaultHandlerTests)
523
524if __name__ == "__main__":
525 test_main()