How to obtain the best results
------------------------------
-For best results, Python should be compiled with
-``CFLAGS="-fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"`` as this allows
+For best results, keep frame pointers enabled. On supported GCC-compatible
+toolchains, CPython builds itself with ``-fno-omit-frame-pointer`` and, when
+available, ``-mno-omit-leaf-frame-pointer`` by default. These flags allow
profilers to unwind using only the frame pointer and not on DWARF debug
information. This is because as the code that is interposed to allow ``perf``
support is dynamically generated it doesn't have any DWARF debugging information
.. versionadded:: 3.14
+.. option:: --without-frame-pointers
+
+ Disable frame pointers, which are enabled by default (see :pep:`831`).
+
+ By default, the build appends ``-fno-omit-frame-pointer`` (and
+ ``-mno-omit-leaf-frame-pointer`` when the compiler supports it) to
+ ``BASECFLAGS`` so profilers, debuggers, and system tracing tools
+ (``perf``, ``eBPF``, ``dtrace``, ``gdb``) can walk the C call stack
+ without DWARF metadata. The flags propagate to third-party C
+ extensions through :mod:`sysconfig`. On compilers that do not
+ understand them, the build silently skips them.
+
+ Downstream packagers and authors of native libraries built with
+ custom build systems should set the same flags so the unwind chain
+ stays unbroken across all native frames.
+
+ .. versionadded:: 3.15
+
.. option:: --without-mimalloc
Disable the fast :ref:`mimalloc <mimalloc>` allocator
* :pep:`782`: :ref:`A new PyBytesWriter C API to create a Python bytes object
<whatsnew315-pybyteswriter>`
* :pep:`803`: :ref:`Stable ABI for Free-Threaded Builds <whatsnew315-abi3t>`
+* :pep:`831`: :ref:`Frame pointers everywhere <whatsnew315-frame-pointers>`
* :ref:`The JIT compiler has been significantly upgraded <whatsnew315-jit>`
* :ref:`Improved error messages <whatsnew315-improved-error-messages>`
* :ref:`The official Windows 64-bit binaries now use the tail-calling interpreter
and :option:`-X dev <-X>` is passed to the Python or Python is built in :ref:`debug mode <debug-build>`.
(Contributed by Donghee Na in :gh:`141770`.)
+.. _whatsnew315-frame-pointers:
+
+* CPython is now built with frame pointers enabled by default
+ (:pep:`831`). Pass :option:`--without-frame-pointers` to opt out.
+ Authors of C extensions and native libraries built with custom build
+ systems should add ``-fno-omit-frame-pointer`` and
+ ``-mno-omit-leaf-frame-pointer`` to their own ``CFLAGS`` to keep the
+ unwind chain intact.
+ (Contributed by Pablo Galindo Salgado and Savannah Ostrowski in :gh:`149201`.)
+
.. _whatsnew315-windows-tail-calling-interpreter:
* 64-bit builds using Visual Studio 2026 (MSVC 18) may now use the new
)
if "no-omit-frame-pointer" in cflags:
- # For example, configure adds -fno-omit-frame-pointer if Python
- # has perf trampoline (PY_HAVE_PERF_TRAMPOLINE) and Python is built
- # in debug mode.
+ # For example, configure adds -fno-omit-frame-pointer by default on
+ # supported GCC-compatible builds.
return True
if "omit-frame-pointer" in cflags:
return False
--- /dev/null
+Enable frame pointers by default for GCC-compatible CPython builds, including
+``-mno-omit-leaf-frame-pointer`` when the compiler supports it, so profilers
+and debuggers can unwind native interpreter frames more reliably. Users can pass
+``--without-frame-pointers`` to opt out.
with_strict_overflow
enable_safety
enable_slower_safety
+with_frame_pointers
enable_experimental_jit
with_dsymutil
with_address_sanitizer
is no)
--with-strict-overflow if 'yes', add -fstrict-overflow to CFLAGS, else add
-fno-strict-overflow (default is no)
+ --without-frame-pointers
+ build without frame pointers (default is no)
--with-dsymutil link debug information into final executable with
dsymutil in macOS (default is no)
--with-address-sanitizer
fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to build with frame pointers" >&5
+printf %s "checking whether to build with frame pointers... " >&6; }
+
+# Check whether --with-frame-pointers was given.
+if test ${with_frame_pointers+y}
+then :
+ withval=$with_frame_pointers;
+else case e in #(
+ e) with_frame_pointers=yes ;;
+esac
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_frame_pointers" >&5
+printf "%s\n" "$with_frame_pointers" >&6; }
+
if test "x$ac_cv_gcc_compat" = xyes
then :
+ frame_pointer_cflags=
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fno-omit-frame-pointer" >&5
+printf %s "checking whether C compiler accepts -fno-omit-frame-pointer... " >&6; }
+if test ${ax_cv_check_cflags__Werror__fno_omit_frame_pointer+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e)
+ ax_check_save_flags=$CFLAGS
+ CFLAGS="$CFLAGS -Werror -fno-omit-frame-pointer"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ax_cv_check_cflags__Werror__fno_omit_frame_pointer=yes
+else case e in #(
+ e) ax_cv_check_cflags__Werror__fno_omit_frame_pointer=no ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ CFLAGS=$ax_check_save_flags ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__fno_omit_frame_pointer" >&5
+printf "%s\n" "$ax_cv_check_cflags__Werror__fno_omit_frame_pointer" >&6; }
+if test "x$ax_cv_check_cflags__Werror__fno_omit_frame_pointer" = xyes
+then :
+
+ frame_pointer_cflags="-fno-omit-frame-pointer"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -mno-omit-leaf-frame-pointer" >&5
+printf %s "checking whether C compiler accepts -mno-omit-leaf-frame-pointer... " >&6; }
+if test ${ax_cv_check_cflags__Werror__mno_omit_leaf_frame_pointer+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e)
+ ax_check_save_flags=$CFLAGS
+ CFLAGS="$CFLAGS -Werror -mno-omit-leaf-frame-pointer"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ax_cv_check_cflags__Werror__mno_omit_leaf_frame_pointer=yes
+else case e in #(
+ e) ax_cv_check_cflags__Werror__mno_omit_leaf_frame_pointer=no ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ CFLAGS=$ax_check_save_flags ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__mno_omit_leaf_frame_pointer" >&5
+printf "%s\n" "$ax_cv_check_cflags__Werror__mno_omit_leaf_frame_pointer" >&6; }
+if test "x$ax_cv_check_cflags__Werror__mno_omit_leaf_frame_pointer" = xyes
+then :
+
+ frame_pointer_cflags="$frame_pointer_cflags -mno-omit-leaf-frame-pointer"
+
+else case e in #(
+ e) : ;;
+esac
+fi
+
+
+else case e in #(
+ e) : ;;
+esac
+fi
+
+ if test -n "$frame_pointer_cflags" && test "x$with_frame_pointers" != xno; then
+ BASECFLAGS="$frame_pointer_cflags $BASECFLAGS"
+ fi
+
CFLAGS_NODIST="$CFLAGS_NODIST -std=c11"
PERF_TRAMPOLINE_OBJ=Python/asm_trampoline.o
- if test "x$Py_DEBUG" = xtrue
-then :
-
- as_fn_append BASECFLAGS " -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"
-
-fi
-
fi
AX_CHECK_COMPILE_FLAG([-D_FORTIFY_SOURCE=3], [CFLAGS_NODIST="$CFLAGS_NODIST -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3"], [AC_MSG_WARN([-D_FORTIFY_SOURCE=3 not supported])], [-Werror])
fi
+AC_MSG_CHECKING([whether to build with frame pointers])
+AC_ARG_WITH([frame-pointers],
+ [AS_HELP_STRING([--without-frame-pointers],
+ [build without frame pointers (default is no)])],
+ [],
+ [with_frame_pointers=yes])
+AC_MSG_RESULT([$with_frame_pointers])
+
AS_VAR_IF([ac_cv_gcc_compat], [yes], [
+ dnl Keep frame pointers in CPython, stdlib objects, and third-party
+ dnl extensions built against this Python (BASECFLAGS propagates via
+ dnl sysconfig) so native profilers can unwind interpreter frames and
+ dnl generated trampolines without DWARF.
+ frame_pointer_cflags=
+ AX_CHECK_COMPILE_FLAG([-fno-omit-frame-pointer], [
+ frame_pointer_cflags="-fno-omit-frame-pointer"
+ AX_CHECK_COMPILE_FLAG([-mno-omit-leaf-frame-pointer], [
+ frame_pointer_cflags="$frame_pointer_cflags -mno-omit-leaf-frame-pointer"
+ ], [], [-Werror])
+ ], [], [-Werror])
+ if test -n "$frame_pointer_cflags" && test "x$with_frame_pointers" != xno; then
+ BASECFLAGS="$frame_pointer_cflags $BASECFLAGS"
+ fi
+
CFLAGS_NODIST="$CFLAGS_NODIST -std=c11"
PY_CHECK_CC_WARNING([enable], [extra], [if we can add -Wextra])
AS_VAR_IF([perf_trampoline], [yes], [
AC_DEFINE([PY_HAVE_PERF_TRAMPOLINE], [1], [Define to 1 if you have the perf trampoline.])
PERF_TRAMPOLINE_OBJ=Python/asm_trampoline.o
-
- dnl perf needs frame pointers for unwinding, include compiler option in debug builds
- AS_VAR_IF([Py_DEBUG], [true], [
- AS_VAR_APPEND([BASECFLAGS], [" -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"])
- ])
])
AC_SUBST([PERF_TRAMPOLINE_OBJ])