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:
parent
da57599af5
commit
0a963fbc9c
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 = []
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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')
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Reference in New Issue