aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--Doc/library/faulthandler.rst40
-rw-r--r--Doc/whatsnew/3.14.rst9
-rw-r--r--Include/internal/pycore_faulthandler.h1
-rw-r--r--Include/internal/pycore_traceback.h3
-rw-r--r--Lib/test/test_faulthandler.py40
-rw-r--r--Lib/test/test_inspect/test_inspect.py2
-rw-r--r--Misc/NEWS.d/next/Library/2024-12-21-13-31-55.gh-issue-127604.etL5mf.rst3
-rw-r--r--Modules/faulthandler.c60
-rw-r--r--Python/traceback.c108
-rw-r--r--Tools/c-analyzer/cpython/ignored.tsv1
-rwxr-xr-xconfigure161
-rw-r--r--configure.ac4
-rw-r--r--pyconfig.h.in15
13 files changed, 378 insertions, 69 deletions
diff --git a/Doc/library/faulthandler.rst b/Doc/library/faulthandler.rst
index b7df9f6b9bc..8823c8f8edf 100644
--- a/Doc/library/faulthandler.rst
+++ b/Doc/library/faulthandler.rst
@@ -66,10 +66,41 @@ Dumping the traceback
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
-------------------
-.. 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`,
: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
: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
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
prevent the risk of data races.
+ .. versionchanged:: next
+ The dump now displays the C stack trace if *c_stack* is true.
+
.. function:: disable()
Disable the fault handler: uninstall the signal handlers installed by
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 51f4286efd6..c9b0c57aa2b 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -699,6 +699,15 @@ errno
(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
-------
diff --git a/Include/internal/pycore_faulthandler.h b/Include/internal/pycore_faulthandler.h
index 6dd7d8d7ca9..78cd657e6ae 100644
--- a/Include/internal/pycore_faulthandler.h
+++ b/Include/internal/pycore_faulthandler.h
@@ -56,6 +56,7 @@ struct _faulthandler_runtime_state {
#ifdef MS_WINDOWS
void *exc_handler;
#endif
+ int c_stack;
} fatal_error;
struct {
diff --git a/Include/internal/pycore_traceback.h b/Include/internal/pycore_traceback.h
index 741108a957a..d71dd288699 100644
--- a/Include/internal/pycore_traceback.h
+++ b/Include/internal/pycore_traceback.h
@@ -99,6 +99,9 @@ extern int _PyTraceBack_Print(
extern int _Py_WriteIndentedMargin(int, const char*, PyObject *);
extern int _Py_WriteIndent(int, PyObject *);
+// Export for the faulthandler module
+PyAPI_FUNC(void) _Py_DumpStack(int fd);
+
#ifdef __cplusplus
}
#endif
diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py
index 2a8c96f049e..371c63adce9 100644
--- a/Lib/test/test_faulthandler.py
+++ b/Lib/test/test_faulthandler.py
@@ -55,6 +55,13 @@ def temporary_filename():
finally:
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):
def get_output(self, code, filename=None, fd=None):
@@ -103,6 +110,7 @@ class FaultHandlerTests(unittest.TestCase):
fd=None, know_current_thread=True,
py_fatal_error=False,
garbage_collecting=False,
+ c_stack=True,
function='<module>'):
"""
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:
regex.append(' Garbage-collecting')
regex.append(fr' File "<string>", line {lineno} in {function}')
+ if c_stack:
+ regex.extend(C_STACK_REGEX)
regex = '\n'.join(regex)
if other_regex:
@@ -950,5 +960,35 @@ class FaultHandlerTests(unittest.TestCase):
_, exitcode = self.get_output(code)
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__":
unittest.main()
diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py
index daae990458d..06f0ca36f97 100644
--- a/Lib/test/test_inspect/test_inspect.py
+++ b/Lib/test/test_inspect/test_inspect.py
@@ -5760,7 +5760,7 @@ class TestSignatureDefinitions(unittest.TestCase):
def test_faulthandler_module_has_signatures(self):
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']
if hasattr(faulthandler, name)}
self._test_module_has_signatures(faulthandler, unsupported_signature=unsupported_signature)
diff --git a/Misc/NEWS.d/next/Library/2024-12-21-13-31-55.gh-issue-127604.etL5mf.rst b/Misc/NEWS.d/next/Library/2024-12-21-13-31-55.gh-issue-127604.etL5mf.rst
new file mode 100644
index 00000000000..c4d2938a500
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-12-21-13-31-55.gh-issue-127604.etL5mf.rst
@@ -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`.
diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c
index cc5ecdcc4f9..563ffd9fbbd 100644
--- a/Modules/faulthandler.c
+++ b/Modules/faulthandler.c
@@ -9,10 +9,10 @@
#include "pycore_sysmodule.h" // _PySys_GetRequiredAttr()
#include "pycore_time.h" // _PyTime_FromSecondsObject()
#include "pycore_traceback.h" // _Py_DumpTracebackThreads
-
#ifdef HAVE_UNISTD_H
# include <unistd.h> // _exit()
#endif
+
#include <signal.h> // sigaction()
#include <stdlib.h> // abort()
#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;
}
+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*
faulthandler_dump_traceback_py(PyObject *self,
PyObject *args, PyObject *kwargs)
@@ -260,6 +279,33 @@ faulthandler_dump_traceback_py(PyObject *self,
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
faulthandler_disable_fatal_handler(fault_handler_t *handler)
{
@@ -350,6 +396,7 @@ faulthandler_fatal_error(int signum)
faulthandler_dump_traceback(fd, deduce_all_threads(),
fatal_error.interp);
+ faulthandler_dump_c_stack(fd);
_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(),
fatal_error.interp);
+ faulthandler_dump_c_stack(fd);
/* call the next exception handler */
return EXCEPTION_CONTINUE_SEARCH;
@@ -519,14 +567,15 @@ faulthandler_enable(void)
static PyObject*
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;
int all_threads = 1;
int fd;
+ int c_stack = 1;
PyThreadState *tstate;
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
- "|Op:enable", kwlist, &file, &all_threads))
+ "|Opp:enable", kwlist, &file, &all_threads, &c_stack))
return NULL;
fd = faulthandler_get_fileno(&file);
@@ -543,6 +592,7 @@ faulthandler_py_enable(PyObject *self, PyObject *args, PyObject *kwargs)
fatal_error.fd = fd;
fatal_error.all_threads = all_threads;
fatal_error.interp = PyThreadState_GetInterpreter(tstate);
+ fatal_error.c_stack = c_stack;
if (faulthandler_enable() < 0) {
return NULL;
@@ -1238,6 +1288,10 @@ static PyMethodDef module_methods[] = {
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 "
"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",
_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"
diff --git a/Python/traceback.c b/Python/traceback.c
index d30ba586cfc..7319382f053 100644
--- a/Python/traceback.c
+++ b/Python/traceback.c
@@ -18,7 +18,25 @@
#ifdef HAVE_UNISTD_H
# include <unistd.h> // lseek()
#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 PUTS(fd, str) (void)_Py_write_noraise(fd, str, strlen(str))
@@ -1166,3 +1184,93 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
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
diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv
index 14dc5007b65..a33619b1b34 100644
--- a/Tools/c-analyzer/cpython/ignored.tsv
+++ b/Tools/c-analyzer/cpython/ignored.tsv
@@ -166,6 +166,7 @@ Python/sysmodule.c - _preinit_xoptions -
# thread-safety
# XXX need race protection?
Modules/faulthandler.c faulthandler_dump_traceback reentrant -
+Modules/faulthandler.c faulthandler_dump_c_stack reentrant -
Python/pylifecycle.c _Py_FatalErrorFormat reentrant -
Python/pylifecycle.c fatal_error reentrant -
diff --git a/configure b/configure
index 284263dff48..2685fdcd7b4 100755
--- a/configure
+++ b/configure
@@ -2313,6 +2313,70 @@ fi
} # 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
# -------------------------------------------
# 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_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
# ------------------------------------------------------------------
# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
@@ -11877,6 +11877,39 @@ 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.
# http://permalink.gmane.org/gmane.linux.bluez.kernel/22294
SAVE_CFLAGS=$CFLAGS
diff --git a/configure.ac b/configure.ac
index 99ebe111cfb..55b499faa8d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2985,6 +2985,10 @@ AC_CHECK_HEADERS([ \
AC_HEADER_DIRENT
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.
# http://permalink.gmane.org/gmane.linux.bluez.kernel/22294
SAVE_CFLAGS=$CFLAGS
diff --git a/pyconfig.h.in b/pyconfig.h.in
index aa086d49e90..c8a0174bd38 100644
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -89,6 +89,12 @@
/* Define to 1 if you have the 'atanh' function. */
#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. */
#undef HAVE_BIND
@@ -289,6 +295,9 @@
/* Define to 1 if you have the 'dladdr' function. */
#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. */
#undef HAVE_DLFCN_H
@@ -334,6 +343,9 @@
/* Define if you have the 'eventfd' function. */
#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. */
#undef HAVE_EXECV
@@ -705,6 +717,9 @@
/* Define to 1 if you have the 'linkat' function. */
#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. */
#undef HAVE_LINUX_AUXVEC_H