bpo-38203: faulthandler.dump_traceback_later() is always available (GH-16249)

dump_traceback_later() and cancel_dump_traceback_later() functions of
the faulthandler module are always available since Python 3.7.
This commit is contained in:
Victor Stinner 2019-09-18 14:15:10 +02:00 committed by GitHub
parent da57599af5
commit 0a963fbc9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 10 additions and 38 deletions

View File

@ -100,8 +100,10 @@ Dumping the tracebacks after a timeout
:func:`cancel_dump_traceback_later` is called: see :ref:`issue with file :func:`cancel_dump_traceback_later` is called: see :ref:`issue with file
descriptors <faulthandler-fd>`. descriptors <faulthandler-fd>`.
This function is implemented using a watchdog thread and therefore is not This function is implemented using a watchdog thread.
available if Python is compiled with threads disabled.
.. versionchanged:: 3.7
This function is now always available.
.. versionchanged:: 3.5 .. versionchanged:: 3.5
Added support for passing file descriptor to this function. Added support for passing file descriptor to this function.

View File

@ -57,9 +57,8 @@ class EINTRBaseTest(unittest.TestCase):
# Use faulthandler as watchdog to debug when a test hangs # Use faulthandler as watchdog to debug when a test hangs
# (timeout of 10 minutes) # (timeout of 10 minutes)
if hasattr(faulthandler, 'dump_traceback_later'): faulthandler.dump_traceback_later(10 * 60, exit=True,
faulthandler.dump_traceback_later(10 * 60, exit=True, file=sys.__stderr__)
file=sys.__stderr__)
@staticmethod @staticmethod
def stop_alarm(): def stop_alarm():
@ -68,8 +67,7 @@ class EINTRBaseTest(unittest.TestCase):
def tearDown(self): def tearDown(self):
self.stop_alarm() self.stop_alarm()
signal.signal(signal.SIGALRM, self.orig_handler) signal.signal(signal.SIGALRM, self.orig_handler)
if hasattr(faulthandler, 'cancel_dump_traceback_later'): faulthandler.cancel_dump_traceback_later()
faulthandler.cancel_dump_traceback_later()
def subprocess(self, *args, **kw): def subprocess(self, *args, **kw):
cmd_args = (sys.executable, '-c') + args cmd_args = (sys.executable, '-c') + args

View File

@ -164,11 +164,6 @@ class Regrtest:
def parse_args(self, kwargs): def parse_args(self, kwargs):
ns = _parse_args(sys.argv[1:], **kwargs) ns = _parse_args(sys.argv[1:], **kwargs)
if ns.timeout and not hasattr(faulthandler, 'dump_traceback_later'):
print("Warning: The timeout option requires "
"faulthandler.dump_traceback_later", file=sys.stderr)
ns.timeout = None
if ns.xmlpath: if ns.xmlpath:
support.junit_xml_list = self.testsuite_xml = [] support.junit_xml_list = self.testsuite_xml = []

View File

@ -535,8 +535,6 @@ class FaultHandlerTests(unittest.TestCase):
with temporary_filename() as filename: with temporary_filename() as filename:
self.check_dump_traceback_threads(filename) self.check_dump_traceback_threads(filename)
@unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'),
'need faulthandler.dump_traceback_later()')
def check_dump_traceback_later(self, repeat=False, cancel=False, loops=1, def check_dump_traceback_later(self, repeat=False, cancel=False, loops=1,
*, filename=None, fd=None): *, filename=None, fd=None):
""" """
@ -744,9 +742,8 @@ class FaultHandlerTests(unittest.TestCase):
faulthandler.enable() faulthandler.enable()
with self.check_stderr_none(): with self.check_stderr_none():
faulthandler.dump_traceback() faulthandler.dump_traceback()
if hasattr(faulthandler, 'dump_traceback_later'): with self.check_stderr_none():
with self.check_stderr_none(): faulthandler.dump_traceback_later(1e-3)
faulthandler.dump_traceback_later(1e-3)
if hasattr(faulthandler, "register"): if hasattr(faulthandler, "register"):
with self.check_stderr_none(): with self.check_stderr_none():
faulthandler.register(signal.SIGUSR1) faulthandler.register(signal.SIGUSR1)

View File

@ -54,8 +54,6 @@ class ParseArgsTestCase(unittest.TestCase):
libregrtest._parse_args([opt]) libregrtest._parse_args([opt])
self.assertIn('Run Python regression tests.', out.getvalue()) self.assertIn('Run Python regression tests.', out.getvalue())
@unittest.skipUnless(hasattr(faulthandler, 'dump_traceback_later'),
"faulthandler.dump_traceback_later() required")
def test_timeout(self): def test_timeout(self):
ns = libregrtest._parse_args(['--timeout', '4.2']) ns = libregrtest._parse_args(['--timeout', '4.2'])
self.assertEqual(ns.timeout, 4.2) self.assertEqual(ns.timeout, 4.2)
@ -572,8 +570,7 @@ class ProgramsTestCase(BaseTestCase):
self.python_args = ['-Wd', '-E', '-bb'] self.python_args = ['-Wd', '-E', '-bb']
self.regrtest_args = ['-uall', '-rwW', self.regrtest_args = ['-uall', '-rwW',
'--testdir=%s' % self.tmptestdir] '--testdir=%s' % self.tmptestdir]
if hasattr(faulthandler, 'dump_traceback_later'): self.regrtest_args.extend(('--timeout', '3600', '-j4'))
self.regrtest_args.extend(('--timeout', '3600', '-j4'))
if sys.platform == 'win32': if sys.platform == 'win32':
self.regrtest_args.append('-n') self.regrtest_args.append('-n')

View File

@ -19,8 +19,6 @@
/* Allocate at maximum 100 MiB of the stack to raise the stack overflow */ /* Allocate at maximum 100 MiB of the stack to raise the stack overflow */
#define STACK_OVERFLOW_MAX_SIZE (100 * 1024 * 1024) #define STACK_OVERFLOW_MAX_SIZE (100 * 1024 * 1024)
#define FAULTHANDLER_LATER
#ifndef MS_WINDOWS #ifndef MS_WINDOWS
/* register() is useless on Windows, because only SIGSEGV, SIGABRT and /* register() is useless on Windows, because only SIGSEGV, SIGABRT and
SIGILL can be handled by the process, and these signals can only be used SIGILL can be handled by the process, and these signals can only be used
@ -60,7 +58,6 @@ static struct {
#endif #endif
} fatal_error = {0, NULL, -1, 0}; } fatal_error = {0, NULL, -1, 0};
#ifdef FAULTHANDLER_LATER
static struct { static struct {
PyObject *file; PyObject *file;
int fd; int fd;
@ -77,7 +74,6 @@ static struct {
/* released by child thread when joined */ /* released by child thread when joined */
PyThread_type_lock running; PyThread_type_lock running;
} thread; } thread;
#endif
#ifdef FAULTHANDLER_USER #ifdef FAULTHANDLER_USER
typedef struct { typedef struct {
@ -589,8 +585,6 @@ faulthandler_is_enabled(PyObject *self, PyObject *Py_UNUSED(ignored))
return PyBool_FromLong(fatal_error.enabled); return PyBool_FromLong(fatal_error.enabled);
} }
#ifdef FAULTHANDLER_LATER
static void static void
faulthandler_thread(void *unused) faulthandler_thread(void *unused)
{ {
@ -790,7 +784,6 @@ faulthandler_cancel_dump_traceback_later_py(PyObject *self,
cancel_dump_traceback_later(); cancel_dump_traceback_later();
Py_RETURN_NONE; Py_RETURN_NONE;
} }
#endif /* FAULTHANDLER_LATER */
#ifdef FAULTHANDLER_USER #ifdef FAULTHANDLER_USER
@ -1230,9 +1223,7 @@ faulthandler_stack_overflow(PyObject *self, PyObject *Py_UNUSED(ignored))
static int static int
faulthandler_traverse(PyObject *module, visitproc visit, void *arg) faulthandler_traverse(PyObject *module, visitproc visit, void *arg)
{ {
#ifdef FAULTHANDLER_LATER
Py_VISIT(thread.file); Py_VISIT(thread.file);
#endif
#ifdef FAULTHANDLER_USER #ifdef FAULTHANDLER_USER
if (user_signals != NULL) { if (user_signals != NULL) {
for (size_t signum=0; signum < NSIG; signum++) for (size_t signum=0; signum < NSIG; signum++)
@ -1273,7 +1264,6 @@ static PyMethodDef module_methods[] = {
PyDoc_STR("dump_traceback(file=sys.stderr, all_threads=True): " PyDoc_STR("dump_traceback(file=sys.stderr, all_threads=True): "
"dump the traceback of the current thread, or of all threads " "dump the traceback of the current thread, or of all threads "
"if all_threads is True, into file")}, "if all_threads is True, into file")},
#ifdef FAULTHANDLER_LATER
{"dump_traceback_later", {"dump_traceback_later",
(PyCFunction)(void(*)(void))faulthandler_dump_traceback_later, METH_VARARGS|METH_KEYWORDS, (PyCFunction)(void(*)(void))faulthandler_dump_traceback_later, METH_VARARGS|METH_KEYWORDS,
PyDoc_STR("dump_traceback_later(timeout, repeat=False, file=sys.stderrn, exit=False):\n" PyDoc_STR("dump_traceback_later(timeout, repeat=False, file=sys.stderrn, exit=False):\n"
@ -1284,8 +1274,6 @@ static PyMethodDef module_methods[] = {
faulthandler_cancel_dump_traceback_later_py, METH_NOARGS, faulthandler_cancel_dump_traceback_later_py, METH_NOARGS,
PyDoc_STR("cancel_dump_traceback_later():\ncancel the previous call " PyDoc_STR("cancel_dump_traceback_later():\ncancel the previous call "
"to dump_traceback_later().")}, "to dump_traceback_later().")},
#endif
#ifdef FAULTHANDLER_USER #ifdef FAULTHANDLER_USER
{"register", {"register",
(PyCFunction)(void(*)(void))faulthandler_register_py, METH_VARARGS|METH_KEYWORDS, (PyCFunction)(void(*)(void))faulthandler_register_py, METH_VARARGS|METH_KEYWORDS,
@ -1298,7 +1286,6 @@ static PyMethodDef module_methods[] = {
PyDoc_STR("unregister(signum): unregister the handler of the signal " PyDoc_STR("unregister(signum): unregister the handler of the signal "
"'signum' registered by register()")}, "'signum' registered by register()")},
#endif #endif
{"_read_null", faulthandler_read_null, METH_NOARGS, {"_read_null", faulthandler_read_null, METH_NOARGS,
PyDoc_STR("_read_null(): read from NULL, raise " PyDoc_STR("_read_null(): read from NULL, raise "
"a SIGSEGV or SIGBUS signal depending on the platform")}, "a SIGSEGV or SIGBUS signal depending on the platform")},
@ -1399,9 +1386,7 @@ _PyFaulthandler_Init(int enable)
stack.ss_size = SIGSTKSZ * 2; stack.ss_size = SIGSTKSZ * 2;
#endif #endif
#ifdef FAULTHANDLER_LATER
memset(&thread, 0, sizeof(thread)); memset(&thread, 0, sizeof(thread));
#endif
if (enable) { if (enable) {
if (faulthandler_init_enable() < 0) { if (faulthandler_init_enable() < 0) {
@ -1413,7 +1398,6 @@ _PyFaulthandler_Init(int enable)
void _PyFaulthandler_Fini(void) void _PyFaulthandler_Fini(void)
{ {
#ifdef FAULTHANDLER_LATER
/* later */ /* later */
if (thread.cancel_event) { if (thread.cancel_event) {
cancel_dump_traceback_later(); cancel_dump_traceback_later();
@ -1425,7 +1409,6 @@ void _PyFaulthandler_Fini(void)
PyThread_free_lock(thread.running); PyThread_free_lock(thread.running);
thread.running = NULL; thread.running = NULL;
} }
#endif
#ifdef FAULTHANDLER_USER #ifdef FAULTHANDLER_USER
/* user */ /* user */