https://github.com/python/cpython/commit/8dfa840773d1d6bae1bf6e0dfa07618ea10c9d71
commit: 8dfa840773d1d6bae1bf6e0dfa07618ea10c9d71
branch: main
author: Peter Bierma <zintensity...@gmail.com>
committer: pablogsal <pablog...@gmail.com>
date: 2025-04-21T20:48:02+01:00
summary:

gh-127604: Add C stack dumps to `faulthandler` (#128159)

files:
A Misc/NEWS.d/next/Library/2024-12-21-13-31-55.gh-issue-127604.etL5mf.rst
M Doc/library/faulthandler.rst
M Doc/whatsnew/3.14.rst
M Include/internal/pycore_faulthandler.h
M Include/internal/pycore_traceback.h
M Lib/test/test_faulthandler.py
M Lib/test/test_inspect/test_inspect.py
M Modules/faulthandler.c
M Python/traceback.c
M Tools/c-analyzer/cpython/ignored.tsv
M configure
M configure.ac
M pyconfig.h.in

diff --git a/Doc/library/faulthandler.rst b/Doc/library/faulthandler.rst
index b7df9f6b9bcf96..8823c8f8edf567 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 51f4286efd6785..c9b0c57aa2ba04 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 6dd7d8d7ca9792..78cd657e6ae5ae 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 741108a957a35c..d71dd2886999a6 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 2a8c96f049efd0..371c63adce9412 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 @@ def check_error(self, code, lineno, fatal_error, *,
                     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 @@ def check_error(self, code, lineno, fatal_error, *,
             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 @@ def run(self):
         _, 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 daae990458d708..06f0ca36f9796f 100644
--- a/Lib/test/test_inspect/test_inspect.py
+++ b/Lib/test/test_inspect/test_inspect.py
@@ -5760,7 +5760,7 @@ def test_errno_module_has_signatures(self):
 
     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 00000000000000..c4d2938a50010d
--- /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 cc5ecdcc4f96e9..563ffd9fbbdadb 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 d30ba586cfcca4..7319382f053eae 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 14dc5007b65861..a33619b1b345e2 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 284263dff48f87..2685fdcd7b435f 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 99ebe111cfb184..55b499faa8d06e 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 aa086d49e90a5b..c8a0174bd38aee 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
 

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to