mirror of
https://github.com/python/cpython.git
synced 2025-08-27 04:05:34 +00:00
gh-127604: Add C stack dumps to faulthandler
(#128159)
This commit is contained in:
parent
ea8ec95cfa
commit
8dfa840773
13 changed files with 378 additions and 69 deletions
|
@ -66,10 +66,41 @@ Dumping the traceback
|
||||||
Added support for passing file descriptor to this function.
|
Added support for passing file descriptor to this function.
|
||||||
|
|
||||||
|
|
||||||
|
Dumping the C stack
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
|
.. function:: dump_c_stack(file=sys.stderr)
|
||||||
|
|
||||||
|
Dump the C stack trace of the current thread into *file*.
|
||||||
|
|
||||||
|
If the Python build does not support it or the operating system
|
||||||
|
does not provide a stack trace, then this prints an error in place
|
||||||
|
of a dumped C stack.
|
||||||
|
|
||||||
|
.. _c-stack-compatibility:
|
||||||
|
|
||||||
|
C Stack Compatibility
|
||||||
|
*********************
|
||||||
|
|
||||||
|
If the system does not support the C-level :manpage:`backtrace(3)`,
|
||||||
|
:manpage:`backtrace_symbols(3)`, or :manpage:`dladdr(3)`, then C stack dumps
|
||||||
|
will not work. An error will be printed instead of the stack.
|
||||||
|
|
||||||
|
Additionally, some compilers do not support :term:`CPython's <CPython>`
|
||||||
|
implementation of C stack dumps. As a result, a different error may be printed
|
||||||
|
instead of the stack, even if the the operating system supports dumping stacks.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Dumping C stacks can be arbitrarily slow, depending on the DWARF level
|
||||||
|
of the binaries in the call stack.
|
||||||
|
|
||||||
Fault handler state
|
Fault handler state
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
.. function:: enable(file=sys.stderr, all_threads=True)
|
.. function:: enable(file=sys.stderr, all_threads=True, c_stack=True)
|
||||||
|
|
||||||
Enable the fault handler: install handlers for the :const:`~signal.SIGSEGV`,
|
Enable the fault handler: install handlers for the :const:`~signal.SIGSEGV`,
|
||||||
:const:`~signal.SIGFPE`, :const:`~signal.SIGABRT`, :const:`~signal.SIGBUS`
|
:const:`~signal.SIGFPE`, :const:`~signal.SIGABRT`, :const:`~signal.SIGBUS`
|
||||||
|
@ -81,6 +112,10 @@ Fault handler state
|
||||||
The *file* must be kept open until the fault handler is disabled: see
|
The *file* must be kept open until the fault handler is disabled: see
|
||||||
:ref:`issue with file descriptors <faulthandler-fd>`.
|
:ref:`issue with file descriptors <faulthandler-fd>`.
|
||||||
|
|
||||||
|
If *c_stack* is ``True``, then the C stack trace is printed after the Python
|
||||||
|
traceback, unless the system does not support it. See :func:`dump_c_stack` for
|
||||||
|
more information on compatibility.
|
||||||
|
|
||||||
.. versionchanged:: 3.5
|
.. versionchanged:: 3.5
|
||||||
Added support for passing file descriptor to this function.
|
Added support for passing file descriptor to this function.
|
||||||
|
|
||||||
|
@ -95,6 +130,9 @@ Fault handler state
|
||||||
Only the current thread is dumped if the :term:`GIL` is disabled to
|
Only the current thread is dumped if the :term:`GIL` is disabled to
|
||||||
prevent the risk of data races.
|
prevent the risk of data races.
|
||||||
|
|
||||||
|
.. versionchanged:: next
|
||||||
|
The dump now displays the C stack trace if *c_stack* is true.
|
||||||
|
|
||||||
.. function:: disable()
|
.. function:: disable()
|
||||||
|
|
||||||
Disable the fault handler: uninstall the signal handlers installed by
|
Disable the fault handler: uninstall the signal handlers installed by
|
||||||
|
|
|
@ -699,6 +699,15 @@ errno
|
||||||
(Contributed by James Roy in :gh:`126585`.)
|
(Contributed by James Roy in :gh:`126585`.)
|
||||||
|
|
||||||
|
|
||||||
|
faulthandler
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Add support for printing the C stack trace on systems that
|
||||||
|
:ref:`support it <c-stack-compatibility>` via :func:`faulthandler.dump_c_stack`
|
||||||
|
or via the *c_stack* argument in :func:`faulthandler.enable`.
|
||||||
|
(Contributed by Peter Bierma in :gh:`127604`.)
|
||||||
|
|
||||||
|
|
||||||
fnmatch
|
fnmatch
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ struct _faulthandler_runtime_state {
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
void *exc_handler;
|
void *exc_handler;
|
||||||
#endif
|
#endif
|
||||||
|
int c_stack;
|
||||||
} fatal_error;
|
} fatal_error;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
|
@ -99,6 +99,9 @@ extern int _PyTraceBack_Print(
|
||||||
extern int _Py_WriteIndentedMargin(int, const char*, PyObject *);
|
extern int _Py_WriteIndentedMargin(int, const char*, PyObject *);
|
||||||
extern int _Py_WriteIndent(int, PyObject *);
|
extern int _Py_WriteIndent(int, PyObject *);
|
||||||
|
|
||||||
|
// Export for the faulthandler module
|
||||||
|
PyAPI_FUNC(void) _Py_DumpStack(int fd);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -55,6 +55,13 @@ def temporary_filename():
|
||||||
finally:
|
finally:
|
||||||
os_helper.unlink(filename)
|
os_helper.unlink(filename)
|
||||||
|
|
||||||
|
|
||||||
|
ADDRESS_EXPR = "0x[0-9a-f]+"
|
||||||
|
C_STACK_REGEX = [
|
||||||
|
r"Current thread's C stack trace \(most recent call first\):",
|
||||||
|
fr'( Binary file ".+"(, at .*(\+|-){ADDRESS_EXPR})? \[{ADDRESS_EXPR}\])|(<.+>)'
|
||||||
|
]
|
||||||
|
|
||||||
class FaultHandlerTests(unittest.TestCase):
|
class FaultHandlerTests(unittest.TestCase):
|
||||||
|
|
||||||
def get_output(self, code, filename=None, fd=None):
|
def get_output(self, code, filename=None, fd=None):
|
||||||
|
@ -103,6 +110,7 @@ class FaultHandlerTests(unittest.TestCase):
|
||||||
fd=None, know_current_thread=True,
|
fd=None, know_current_thread=True,
|
||||||
py_fatal_error=False,
|
py_fatal_error=False,
|
||||||
garbage_collecting=False,
|
garbage_collecting=False,
|
||||||
|
c_stack=True,
|
||||||
function='<module>'):
|
function='<module>'):
|
||||||
"""
|
"""
|
||||||
Check that the fault handler for fatal errors is enabled and check the
|
Check that the fault handler for fatal errors is enabled and check the
|
||||||
|
@ -134,6 +142,8 @@ class FaultHandlerTests(unittest.TestCase):
|
||||||
if garbage_collecting and not all_threads_disabled:
|
if garbage_collecting and not all_threads_disabled:
|
||||||
regex.append(' Garbage-collecting')
|
regex.append(' Garbage-collecting')
|
||||||
regex.append(fr' File "<string>", line {lineno} in {function}')
|
regex.append(fr' File "<string>", line {lineno} in {function}')
|
||||||
|
if c_stack:
|
||||||
|
regex.extend(C_STACK_REGEX)
|
||||||
regex = '\n'.join(regex)
|
regex = '\n'.join(regex)
|
||||||
|
|
||||||
if other_regex:
|
if other_regex:
|
||||||
|
@ -950,5 +960,35 @@ class FaultHandlerTests(unittest.TestCase):
|
||||||
_, exitcode = self.get_output(code)
|
_, exitcode = self.get_output(code)
|
||||||
self.assertEqual(exitcode, 0)
|
self.assertEqual(exitcode, 0)
|
||||||
|
|
||||||
|
def check_c_stack(self, output):
|
||||||
|
starting_line = output.pop(0)
|
||||||
|
self.assertRegex(starting_line, C_STACK_REGEX[0])
|
||||||
|
self.assertGreater(len(output), 0)
|
||||||
|
|
||||||
|
for line in output:
|
||||||
|
with self.subTest(line=line):
|
||||||
|
if line != '': # Ignore trailing or leading newlines
|
||||||
|
self.assertRegex(line, C_STACK_REGEX[1])
|
||||||
|
|
||||||
|
|
||||||
|
def test_dump_c_stack(self):
|
||||||
|
code = dedent("""
|
||||||
|
import faulthandler
|
||||||
|
faulthandler.dump_c_stack()
|
||||||
|
""")
|
||||||
|
output, exitcode = self.get_output(code)
|
||||||
|
self.assertEqual(exitcode, 0)
|
||||||
|
self.check_c_stack(output)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dump_c_stack_file(self):
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
with tempfile.TemporaryFile("w+") as tmp:
|
||||||
|
faulthandler.dump_c_stack(file=tmp)
|
||||||
|
tmp.flush() # Just in case
|
||||||
|
tmp.seek(0)
|
||||||
|
self.check_c_stack(tmp.read().split("\n"))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -5760,7 +5760,7 @@ class TestSignatureDefinitions(unittest.TestCase):
|
||||||
|
|
||||||
def test_faulthandler_module_has_signatures(self):
|
def test_faulthandler_module_has_signatures(self):
|
||||||
import faulthandler
|
import faulthandler
|
||||||
unsupported_signature = {'dump_traceback', 'dump_traceback_later', 'enable'}
|
unsupported_signature = {'dump_traceback', 'dump_traceback_later', 'enable', 'dump_c_stack'}
|
||||||
unsupported_signature |= {name for name in ['register']
|
unsupported_signature |= {name for name in ['register']
|
||||||
if hasattr(faulthandler, name)}
|
if hasattr(faulthandler, name)}
|
||||||
self._test_module_has_signatures(faulthandler, unsupported_signature=unsupported_signature)
|
self._test_module_has_signatures(faulthandler, unsupported_signature=unsupported_signature)
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Add support for printing the C stack trace on systems that support it via
|
||||||
|
:func:`faulthandler.dump_c_stack` or via the *c_stack* argument in
|
||||||
|
:func:`faulthandler.enable`.
|
|
@ -9,10 +9,10 @@
|
||||||
#include "pycore_sysmodule.h" // _PySys_GetRequiredAttr()
|
#include "pycore_sysmodule.h" // _PySys_GetRequiredAttr()
|
||||||
#include "pycore_time.h" // _PyTime_FromSecondsObject()
|
#include "pycore_time.h" // _PyTime_FromSecondsObject()
|
||||||
#include "pycore_traceback.h" // _Py_DumpTracebackThreads
|
#include "pycore_traceback.h" // _Py_DumpTracebackThreads
|
||||||
|
|
||||||
#ifdef HAVE_UNISTD_H
|
#ifdef HAVE_UNISTD_H
|
||||||
# include <unistd.h> // _exit()
|
# include <unistd.h> // _exit()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <signal.h> // sigaction()
|
#include <signal.h> // sigaction()
|
||||||
#include <stdlib.h> // abort()
|
#include <stdlib.h> // abort()
|
||||||
#if defined(HAVE_PTHREAD_SIGMASK) && !defined(HAVE_BROKEN_PTHREAD_SIGMASK) && defined(HAVE_PTHREAD_H)
|
#if defined(HAVE_PTHREAD_SIGMASK) && !defined(HAVE_BROKEN_PTHREAD_SIGMASK) && defined(HAVE_PTHREAD_H)
|
||||||
|
@ -210,6 +210,25 @@ faulthandler_dump_traceback(int fd, int all_threads,
|
||||||
reentrant = 0;
|
reentrant = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
faulthandler_dump_c_stack(int fd)
|
||||||
|
{
|
||||||
|
static volatile int reentrant = 0;
|
||||||
|
|
||||||
|
if (reentrant) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reentrant = 1;
|
||||||
|
|
||||||
|
if (fatal_error.c_stack) {
|
||||||
|
PUTS(fd, "\n");
|
||||||
|
_Py_DumpStack(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
reentrant = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
faulthandler_dump_traceback_py(PyObject *self,
|
faulthandler_dump_traceback_py(PyObject *self,
|
||||||
PyObject *args, PyObject *kwargs)
|
PyObject *args, PyObject *kwargs)
|
||||||
|
@ -260,6 +279,33 @@ faulthandler_dump_traceback_py(PyObject *self,
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
faulthandler_dump_c_stack_py(PyObject *self,
|
||||||
|
PyObject *args, PyObject *kwargs)
|
||||||
|
{
|
||||||
|
static char *kwlist[] = {"file", NULL};
|
||||||
|
PyObject *file = NULL;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||||
|
"|O:dump_c_stack", kwlist,
|
||||||
|
&file)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd = faulthandler_get_fileno(&file);
|
||||||
|
if (fd < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_Py_DumpStack(fd);
|
||||||
|
|
||||||
|
if (PyErr_CheckSignals()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
faulthandler_disable_fatal_handler(fault_handler_t *handler)
|
faulthandler_disable_fatal_handler(fault_handler_t *handler)
|
||||||
{
|
{
|
||||||
|
@ -350,6 +396,7 @@ faulthandler_fatal_error(int signum)
|
||||||
|
|
||||||
faulthandler_dump_traceback(fd, deduce_all_threads(),
|
faulthandler_dump_traceback(fd, deduce_all_threads(),
|
||||||
fatal_error.interp);
|
fatal_error.interp);
|
||||||
|
faulthandler_dump_c_stack(fd);
|
||||||
|
|
||||||
_Py_DumpExtensionModules(fd, fatal_error.interp);
|
_Py_DumpExtensionModules(fd, fatal_error.interp);
|
||||||
|
|
||||||
|
@ -425,6 +472,7 @@ faulthandler_exc_handler(struct _EXCEPTION_POINTERS *exc_info)
|
||||||
|
|
||||||
faulthandler_dump_traceback(fd, deduce_all_threads(),
|
faulthandler_dump_traceback(fd, deduce_all_threads(),
|
||||||
fatal_error.interp);
|
fatal_error.interp);
|
||||||
|
faulthandler_dump_c_stack(fd);
|
||||||
|
|
||||||
/* call the next exception handler */
|
/* call the next exception handler */
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
@ -519,14 +567,15 @@ faulthandler_enable(void)
|
||||||
static PyObject*
|
static PyObject*
|
||||||
faulthandler_py_enable(PyObject *self, PyObject *args, PyObject *kwargs)
|
faulthandler_py_enable(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
{
|
{
|
||||||
static char *kwlist[] = {"file", "all_threads", NULL};
|
static char *kwlist[] = {"file", "all_threads", "c_stack", NULL};
|
||||||
PyObject *file = NULL;
|
PyObject *file = NULL;
|
||||||
int all_threads = 1;
|
int all_threads = 1;
|
||||||
int fd;
|
int fd;
|
||||||
|
int c_stack = 1;
|
||||||
PyThreadState *tstate;
|
PyThreadState *tstate;
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||||
"|Op:enable", kwlist, &file, &all_threads))
|
"|Opp:enable", kwlist, &file, &all_threads, &c_stack))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
fd = faulthandler_get_fileno(&file);
|
fd = faulthandler_get_fileno(&file);
|
||||||
|
@ -543,6 +592,7 @@ faulthandler_py_enable(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
fatal_error.fd = fd;
|
fatal_error.fd = fd;
|
||||||
fatal_error.all_threads = all_threads;
|
fatal_error.all_threads = all_threads;
|
||||||
fatal_error.interp = PyThreadState_GetInterpreter(tstate);
|
fatal_error.interp = PyThreadState_GetInterpreter(tstate);
|
||||||
|
fatal_error.c_stack = c_stack;
|
||||||
|
|
||||||
if (faulthandler_enable() < 0) {
|
if (faulthandler_enable() < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1238,6 +1288,10 @@ static PyMethodDef module_methods[] = {
|
||||||
PyDoc_STR("dump_traceback($module, /, file=sys.stderr, all_threads=True)\n--\n\n"
|
PyDoc_STR("dump_traceback($module, /, file=sys.stderr, all_threads=True)\n--\n\n"
|
||||||
"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.")},
|
||||||
|
{"dump_c_stack",
|
||||||
|
_PyCFunction_CAST(faulthandler_dump_c_stack_py), METH_VARARGS|METH_KEYWORDS,
|
||||||
|
PyDoc_STR("dump_c_stack($module, /, file=sys.stderr)\n--\n\n"
|
||||||
|
"Dump the C stack of the current thread.")},
|
||||||
{"dump_traceback_later",
|
{"dump_traceback_later",
|
||||||
_PyCFunction_CAST(faulthandler_dump_traceback_later), METH_VARARGS|METH_KEYWORDS,
|
_PyCFunction_CAST(faulthandler_dump_traceback_later), METH_VARARGS|METH_KEYWORDS,
|
||||||
PyDoc_STR("dump_traceback_later($module, /, timeout, repeat=False, file=sys.stderr, exit=False)\n--\n\n"
|
PyDoc_STR("dump_traceback_later($module, /, timeout, repeat=False, file=sys.stderr, exit=False)\n--\n\n"
|
||||||
|
|
|
@ -18,7 +18,25 @@
|
||||||
#ifdef HAVE_UNISTD_H
|
#ifdef HAVE_UNISTD_H
|
||||||
# include <unistd.h> // lseek()
|
# include <unistd.h> // lseek()
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(HAVE_EXECINFO_H) && defined(HAVE_DLFCN_H) && defined(HAVE_LINK_H)
|
||||||
|
# include <execinfo.h> // backtrace(), backtrace_symbols()
|
||||||
|
# include <dlfcn.h> // dladdr1()
|
||||||
|
# include <link.h> // struct DL_info
|
||||||
|
# if defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS) && defined(HAVE_DLADDR1)
|
||||||
|
# define CAN_C_BACKTRACE
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__STDC_NO_VLA__) && (__STDC_NO_VLA__ == 1)
|
||||||
|
/* Use alloca() for VLAs. */
|
||||||
|
# define VLA(type, name, size) type *name = alloca(size)
|
||||||
|
#elif !defined(__STDC_NO_VLA__) || (__STDC_NO_VLA__ == 0)
|
||||||
|
/* Use actual C VLAs.*/
|
||||||
|
# define VLA(type, name, size) type name[size]
|
||||||
|
#elif defined(CAN_C_BACKTRACE)
|
||||||
|
/* VLAs are not possible. Disable C stack trace functions. */
|
||||||
|
# undef CAN_C_BACKTRACE
|
||||||
|
#endif
|
||||||
|
|
||||||
#define OFF(x) offsetof(PyTracebackObject, x)
|
#define OFF(x) offsetof(PyTracebackObject, x)
|
||||||
#define PUTS(fd, str) (void)_Py_write_noraise(fd, str, strlen(str))
|
#define PUTS(fd, str) (void)_Py_write_noraise(fd, str, strlen(str))
|
||||||
|
@ -1166,3 +1184,93 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CAN_C_BACKTRACE
|
||||||
|
/* Based on glibc's implementation of backtrace_symbols(), but only uses stack memory. */
|
||||||
|
void
|
||||||
|
_Py_backtrace_symbols_fd(int fd, void *const *array, Py_ssize_t size)
|
||||||
|
{
|
||||||
|
VLA(Dl_info, info, size);
|
||||||
|
VLA(int, status, size);
|
||||||
|
/* Fill in the information we can get from dladdr() */
|
||||||
|
for (Py_ssize_t i = 0; i < size; ++i) {
|
||||||
|
struct link_map *map;
|
||||||
|
status[i] = dladdr1(array[i], &info[i], (void **)&map, RTLD_DL_LINKMAP);
|
||||||
|
if (status[i] != 0
|
||||||
|
&& info[i].dli_fname != NULL
|
||||||
|
&& info[i].dli_fname[0] != '\0') {
|
||||||
|
/* The load bias is more useful to the user than the load
|
||||||
|
address. The use of these addresses is to calculate an
|
||||||
|
address in the ELF file, so its prelinked bias is not
|
||||||
|
something we want to subtract out */
|
||||||
|
info[i].dli_fbase = (void *) map->l_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Py_ssize_t i = 0; i < size; ++i) {
|
||||||
|
if (status[i] == 0
|
||||||
|
|| info[i].dli_fname == NULL
|
||||||
|
|| info[i].dli_fname[0] == '\0'
|
||||||
|
) {
|
||||||
|
dprintf(fd, " Binary file '<unknown>' [%p]\n", array[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info[i].dli_sname == NULL) {
|
||||||
|
/* We found no symbol name to use, so describe it as
|
||||||
|
relative to the file. */
|
||||||
|
info[i].dli_saddr = info[i].dli_fbase;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info[i].dli_sname == NULL
|
||||||
|
&& info[i].dli_saddr == 0) {
|
||||||
|
dprintf(fd, " Binary file \"%s\" [%p]\n",
|
||||||
|
info[i].dli_fname,
|
||||||
|
array[i]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char sign;
|
||||||
|
ptrdiff_t offset;
|
||||||
|
if (array[i] >= (void *) info[i].dli_saddr) {
|
||||||
|
sign = '+';
|
||||||
|
offset = array[i] - info[i].dli_saddr;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sign = '-';
|
||||||
|
offset = info[i].dli_saddr - array[i];
|
||||||
|
}
|
||||||
|
const char *symbol_name = info[i].dli_sname != NULL ? info[i].dli_sname : "";
|
||||||
|
dprintf(fd, " Binary file \"%s\", at %s%c%#tx [%p]\n",
|
||||||
|
info[i].dli_fname,
|
||||||
|
symbol_name,
|
||||||
|
sign, offset, array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_Py_DumpStack(int fd)
|
||||||
|
{
|
||||||
|
#define BACKTRACE_SIZE 32
|
||||||
|
PUTS(fd, "Current thread's C stack trace (most recent call first):\n");
|
||||||
|
VLA(void *, callstack, BACKTRACE_SIZE);
|
||||||
|
int frames = backtrace(callstack, BACKTRACE_SIZE);
|
||||||
|
if (frames == 0) {
|
||||||
|
// Some systems won't return anything for the stack trace
|
||||||
|
PUTS(fd, " <system returned no stack trace>\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_Py_backtrace_symbols_fd(fd, callstack, frames);
|
||||||
|
if (frames == BACKTRACE_SIZE) {
|
||||||
|
PUTS(fd, " <truncated rest of calls>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef BACKTRACE_SIZE
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void
|
||||||
|
_Py_DumpStack(int fd)
|
||||||
|
{
|
||||||
|
PUTS(fd, "Current thread's C stack trace (most recent call first):\n");
|
||||||
|
PUTS(fd, " <cannot get C stack on this system>\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -166,6 +166,7 @@ Python/sysmodule.c - _preinit_xoptions -
|
||||||
# thread-safety
|
# thread-safety
|
||||||
# XXX need race protection?
|
# XXX need race protection?
|
||||||
Modules/faulthandler.c faulthandler_dump_traceback reentrant -
|
Modules/faulthandler.c faulthandler_dump_traceback reentrant -
|
||||||
|
Modules/faulthandler.c faulthandler_dump_c_stack reentrant -
|
||||||
Python/pylifecycle.c _Py_FatalErrorFormat reentrant -
|
Python/pylifecycle.c _Py_FatalErrorFormat reentrant -
|
||||||
Python/pylifecycle.c fatal_error reentrant -
|
Python/pylifecycle.c fatal_error reentrant -
|
||||||
|
|
||||||
|
|
Can't render this file because it has a wrong number of fields in line 4.
|
161
configure
generated
vendored
161
configure
generated
vendored
|
@ -2313,6 +2313,70 @@ fi
|
||||||
|
|
||||||
} # ac_fn_c_try_run
|
} # ac_fn_c_try_run
|
||||||
|
|
||||||
|
# ac_fn_c_check_func LINENO FUNC VAR
|
||||||
|
# ----------------------------------
|
||||||
|
# Tests whether FUNC exists, setting the cache variable VAR accordingly
|
||||||
|
ac_fn_c_check_func ()
|
||||||
|
{
|
||||||
|
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
|
||||||
|
printf %s "checking for $2... " >&6; }
|
||||||
|
if eval test \${$3+y}
|
||||||
|
then :
|
||||||
|
printf %s "(cached) " >&6
|
||||||
|
else case e in #(
|
||||||
|
e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||||
|
/* end confdefs.h. */
|
||||||
|
/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
|
||||||
|
For example, HP-UX 11i <limits.h> declares gettimeofday. */
|
||||||
|
#define $2 innocuous_$2
|
||||||
|
|
||||||
|
/* System header to define __stub macros and hopefully few prototypes,
|
||||||
|
which can conflict with char $2 (void); below. */
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#undef $2
|
||||||
|
|
||||||
|
/* Override any GCC internal prototype to avoid an error.
|
||||||
|
Use char because int might match the return type of a GCC
|
||||||
|
builtin and then its argument prototype would still apply. */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
#endif
|
||||||
|
char $2 (void);
|
||||||
|
/* The GNU C library defines this for functions which it implements
|
||||||
|
to always fail with ENOSYS. Some functions are actually named
|
||||||
|
something starting with __ and the normal name is an alias. */
|
||||||
|
#if defined __stub_$2 || defined __stub___$2
|
||||||
|
choke me
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
return $2 ();
|
||||||
|
;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_ACEOF
|
||||||
|
if ac_fn_c_try_link "$LINENO"
|
||||||
|
then :
|
||||||
|
eval "$3=yes"
|
||||||
|
else case e in #(
|
||||||
|
e) eval "$3=no" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
rm -f core conftest.err conftest.$ac_objext conftest.beam \
|
||||||
|
conftest$ac_exeext conftest.$ac_ext ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
eval ac_res=\$$3
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
||||||
|
printf "%s\n" "$ac_res" >&6; }
|
||||||
|
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
|
||||||
|
|
||||||
|
} # ac_fn_c_check_func
|
||||||
|
|
||||||
# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
|
# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
|
||||||
# -------------------------------------------
|
# -------------------------------------------
|
||||||
# Tests whether TYPE exists after having included INCLUDES, setting cache
|
# Tests whether TYPE exists after having included INCLUDES, setting cache
|
||||||
|
@ -2567,70 +2631,6 @@ rm -f conftest.val
|
||||||
|
|
||||||
} # ac_fn_c_compute_int
|
} # ac_fn_c_compute_int
|
||||||
|
|
||||||
# ac_fn_c_check_func LINENO FUNC VAR
|
|
||||||
# ----------------------------------
|
|
||||||
# Tests whether FUNC exists, setting the cache variable VAR accordingly
|
|
||||||
ac_fn_c_check_func ()
|
|
||||||
{
|
|
||||||
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
|
|
||||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
|
|
||||||
printf %s "checking for $2... " >&6; }
|
|
||||||
if eval test \${$3+y}
|
|
||||||
then :
|
|
||||||
printf %s "(cached) " >&6
|
|
||||||
else case e in #(
|
|
||||||
e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
|
||||||
/* end confdefs.h. */
|
|
||||||
/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
|
|
||||||
For example, HP-UX 11i <limits.h> declares gettimeofday. */
|
|
||||||
#define $2 innocuous_$2
|
|
||||||
|
|
||||||
/* System header to define __stub macros and hopefully few prototypes,
|
|
||||||
which can conflict with char $2 (void); below. */
|
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
#undef $2
|
|
||||||
|
|
||||||
/* Override any GCC internal prototype to avoid an error.
|
|
||||||
Use char because int might match the return type of a GCC
|
|
||||||
builtin and then its argument prototype would still apply. */
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C"
|
|
||||||
#endif
|
|
||||||
char $2 (void);
|
|
||||||
/* The GNU C library defines this for functions which it implements
|
|
||||||
to always fail with ENOSYS. Some functions are actually named
|
|
||||||
something starting with __ and the normal name is an alias. */
|
|
||||||
#if defined __stub_$2 || defined __stub___$2
|
|
||||||
choke me
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int
|
|
||||||
main (void)
|
|
||||||
{
|
|
||||||
return $2 ();
|
|
||||||
;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
_ACEOF
|
|
||||||
if ac_fn_c_try_link "$LINENO"
|
|
||||||
then :
|
|
||||||
eval "$3=yes"
|
|
||||||
else case e in #(
|
|
||||||
e) eval "$3=no" ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
rm -f core conftest.err conftest.$ac_objext conftest.beam \
|
|
||||||
conftest$ac_exeext conftest.$ac_ext ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
eval ac_res=\$$3
|
|
||||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
|
||||||
printf "%s\n" "$ac_res" >&6; }
|
|
||||||
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
|
|
||||||
|
|
||||||
} # ac_fn_c_check_func
|
|
||||||
|
|
||||||
# ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR
|
# ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
|
# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
|
||||||
|
@ -11877,6 +11877,39 @@ fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# for faulthandler
|
||||||
|
for ac_header in execinfo.h link.h dlfcn.h
|
||||||
|
do :
|
||||||
|
as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | sed "$as_sed_sh"`
|
||||||
|
ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
|
||||||
|
if eval test \"x\$"$as_ac_Header"\" = x"yes"
|
||||||
|
then :
|
||||||
|
cat >>confdefs.h <<_ACEOF
|
||||||
|
#define `printf "%s\n" "HAVE_$ac_header" | sed "$as_sed_cpp"` 1
|
||||||
|
_ACEOF
|
||||||
|
ac_fn_c_check_func "$LINENO" "backtrace" "ac_cv_func_backtrace"
|
||||||
|
if test "x$ac_cv_func_backtrace" = xyes
|
||||||
|
then :
|
||||||
|
printf "%s\n" "#define HAVE_BACKTRACE 1" >>confdefs.h
|
||||||
|
|
||||||
|
fi
|
||||||
|
ac_fn_c_check_func "$LINENO" "backtrace_symbols" "ac_cv_func_backtrace_symbols"
|
||||||
|
if test "x$ac_cv_func_backtrace_symbols" = xyes
|
||||||
|
then :
|
||||||
|
printf "%s\n" "#define HAVE_BACKTRACE_SYMBOLS 1" >>confdefs.h
|
||||||
|
|
||||||
|
fi
|
||||||
|
ac_fn_c_check_func "$LINENO" "dladdr1" "ac_cv_func_dladdr1"
|
||||||
|
if test "x$ac_cv_func_dladdr1" = xyes
|
||||||
|
then :
|
||||||
|
printf "%s\n" "#define HAVE_DLADDR1 1" >>confdefs.h
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
# bluetooth/bluetooth.h has been known to not compile with -std=c99.
|
# bluetooth/bluetooth.h has been known to not compile with -std=c99.
|
||||||
# http://permalink.gmane.org/gmane.linux.bluez.kernel/22294
|
# http://permalink.gmane.org/gmane.linux.bluez.kernel/22294
|
||||||
SAVE_CFLAGS=$CFLAGS
|
SAVE_CFLAGS=$CFLAGS
|
||||||
|
|
|
@ -2985,6 +2985,10 @@ AC_CHECK_HEADERS([ \
|
||||||
AC_HEADER_DIRENT
|
AC_HEADER_DIRENT
|
||||||
AC_HEADER_MAJOR
|
AC_HEADER_MAJOR
|
||||||
|
|
||||||
|
# for faulthandler
|
||||||
|
AC_CHECK_HEADERS([execinfo.h link.h dlfcn.h],
|
||||||
|
[AC_CHECK_FUNCS(backtrace backtrace_symbols dladdr1)])
|
||||||
|
|
||||||
# bluetooth/bluetooth.h has been known to not compile with -std=c99.
|
# bluetooth/bluetooth.h has been known to not compile with -std=c99.
|
||||||
# http://permalink.gmane.org/gmane.linux.bluez.kernel/22294
|
# http://permalink.gmane.org/gmane.linux.bluez.kernel/22294
|
||||||
SAVE_CFLAGS=$CFLAGS
|
SAVE_CFLAGS=$CFLAGS
|
||||||
|
|
|
@ -89,6 +89,12 @@
|
||||||
/* Define to 1 if you have the 'atanh' function. */
|
/* Define to 1 if you have the 'atanh' function. */
|
||||||
#undef HAVE_ATANH
|
#undef HAVE_ATANH
|
||||||
|
|
||||||
|
/* Define to 1 if you have the 'backtrace' function. */
|
||||||
|
#undef HAVE_BACKTRACE
|
||||||
|
|
||||||
|
/* Define to 1 if you have the 'backtrace_symbols' function. */
|
||||||
|
#undef HAVE_BACKTRACE_SYMBOLS
|
||||||
|
|
||||||
/* Define if you have the 'bind' function. */
|
/* Define if you have the 'bind' function. */
|
||||||
#undef HAVE_BIND
|
#undef HAVE_BIND
|
||||||
|
|
||||||
|
@ -289,6 +295,9 @@
|
||||||
/* Define to 1 if you have the 'dladdr' function. */
|
/* Define to 1 if you have the 'dladdr' function. */
|
||||||
#undef HAVE_DLADDR
|
#undef HAVE_DLADDR
|
||||||
|
|
||||||
|
/* Define to 1 if you have the 'dladdr1' function. */
|
||||||
|
#undef HAVE_DLADDR1
|
||||||
|
|
||||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||||
#undef HAVE_DLFCN_H
|
#undef HAVE_DLFCN_H
|
||||||
|
|
||||||
|
@ -334,6 +343,9 @@
|
||||||
/* Define if you have the 'eventfd' function. */
|
/* Define if you have the 'eventfd' function. */
|
||||||
#undef HAVE_EVENTFD
|
#undef HAVE_EVENTFD
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <execinfo.h> header file. */
|
||||||
|
#undef HAVE_EXECINFO_H
|
||||||
|
|
||||||
/* Define to 1 if you have the 'execv' function. */
|
/* Define to 1 if you have the 'execv' function. */
|
||||||
#undef HAVE_EXECV
|
#undef HAVE_EXECV
|
||||||
|
|
||||||
|
@ -705,6 +717,9 @@
|
||||||
/* Define to 1 if you have the 'linkat' function. */
|
/* Define to 1 if you have the 'linkat' function. */
|
||||||
#undef HAVE_LINKAT
|
#undef HAVE_LINKAT
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <link.h> header file. */
|
||||||
|
#undef HAVE_LINK_H
|
||||||
|
|
||||||
/* Define to 1 if you have the <linux/auxvec.h> header file. */
|
/* Define to 1 if you have the <linux/auxvec.h> header file. */
|
||||||
#undef HAVE_LINUX_AUXVEC_H
|
#undef HAVE_LINUX_AUXVEC_H
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue