Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions peps/pep-0831.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ Author: Pablo Galindo Salgado <pablogsal@python.org>,
Savannah Ostrowski <savannah@python.org>,
Diego Russo <diego.russo@arm.com>,
Discussions-To: https://discuss.python.org/t/106958
Status: Accepted
Status: Final
Type: Standards Track
Created: 14-Mar-2026
Python-Version: 3.15
Post-History: `13-Apr-2026 <https://discuss.python.org/t/106958>`__
Resolution: `30-Apr-2026 <https://discuss.python.org/t/106958/6>`__

.. canonical-doc:: :external+py3.15:option:`--without-frame-pointers`

Abstract
========

Expand Down Expand Up @@ -470,11 +472,12 @@ Build System Changes
The following changes are made to ``configure.ac``::

AX_CHECK_COMPILE_FLAG([-fno-omit-frame-pointer],
[CFLAGS="$CFLAGS -fno-omit-frame-pointer"])
[BASECFLAGS="$BASECFLAGS -fno-omit-frame-pointer"])
AX_CHECK_COMPILE_FLAG([-mno-omit-leaf-frame-pointer],
[CFLAGS="$CFLAGS -mno-omit-leaf-frame-pointer"])
[BASECFLAGS="$BASECFLAGS -mno-omit-leaf-frame-pointer"])

Using ``CFLAGS`` ensures:
The flags are prepended to ``BASECFLAGS`` (rather than ``CFLAGS_NODIST``) so
they propagate to third-party builds via ``sysconfig``. This ensures:

1. The flags apply to all ``*.c`` files compiled as part of the interpreter:
the ``python`` binary, ``libpython``, and built-in extension modules under
Expand All @@ -483,6 +486,18 @@ Using ``CFLAGS`` ensures:
extensions built against this Python (via ``pip``, Setuptools, or direct
``sysconfig`` queries) inherit frame pointers by default.

Several architectures need adjustments to produce a walkable frame-pointer
chain:

* On 32-bit ARM, ``-marm`` (GCC) or ``-mno-thumb`` (Clang) is added to force
ARM mode, since GCC's default Thumb prologue does not preserve the
``fp[0]``/``fp[1]`` layout the simple unwinder expects.
* On s390x, ``-mbackchain`` is added *instead* of the frame-pointer flags;
GCC and Clang do not emit a usable backchain on s390x without it.
* On ppc64le, no compiler flags are added: the Power ABI already requires
compilers to maintain a back chain by default, so unwinding works without
``-fno-omit-frame-pointer``.

This is an intentional design choice. For profiling data to be useful, the
frame-pointer chain must be continuous through the entire call stack. A gap at
any C extension boundary is as harmful as a gap in the interpreter itself. By
Expand All @@ -502,7 +517,7 @@ A new ``configure`` option is added::

--without-frame-pointers

When specified, neither flag is added to ``CFLAGS``. This is appropriate for
When specified, neither flag is added to ``BASECFLAGS``. This is appropriate for
deployments that have measured an unacceptable regression on their specific
workload, or for distributions that inject frame-pointer flags at a higher
level and wish to avoid double-specification, analogous to Fedora's per-package
Expand Down Expand Up @@ -550,9 +565,10 @@ recommendation for earlier versions.
Platform Scope
--------------

Both flags are accepted by GCC and Clang on all supported Linux architectures
(x86-64, AArch64, s390x, RISC-V, ARM). On macOS with Apple Silicon, the ARM64
ABI mandates frame pointers; the flags are redundant but harmless.
Both flags are accepted by GCC and Clang on x86-64, AArch64, RISC-V, and
32-bit ARM. s390x and ppc64le require different handling (see above). On
macOS with Apple Silicon, the ARM64 ABI mandates frame pointers; the flags
are redundant but harmless.

On Windows x64, MSVC does not use frame pointers for stack unwinding. Instead,
the Windows x64 ABI mandates ``.pdata`` / ``.xdata`` unwind metadata for every
Expand Down Expand Up @@ -746,15 +762,15 @@ formalised by GCC 4.6 in 2011. The industry has since broadly reversed course:

CPython has not yet adopted this change.

Why Not Use ``CFLAGS_NODIST`` Instead of ``CFLAGS``
---------------------------------------------------
Why Not Use ``CFLAGS_NODIST`` Instead of ``BASECFLAGS``
-------------------------------------------------------

CPython's build system provides ``CFLAGS_NODIST`` specifically for flags that
should apply to the interpreter but not propagate to extension module builds
via ``sysconfig``. Using ``CFLAGS_NODIST`` would confine the overhead to the
interpreter itself.

This PEP deliberately chooses ``CFLAGS`` over ``CFLAGS_NODIST`` because frame
This PEP deliberately chooses ``BASECFLAGS`` over ``CFLAGS_NODIST`` because frame
pointers are only useful when the chain is continuous. Unlike debugging aids
such as sanitizers or assertions, which are useful even when applied to a
single component, a frame-pointer chain with a gap at a C extension boundary
Expand All @@ -772,7 +788,7 @@ not exhibit the same call density and sees negligible overhead.
As Gregory Szorc (``python-build-standalone`` creator) noted: "Turning the
corner on the long tail of compiled extensions having frame pointers will take
years. So the sooner we start..." [#pbs992]_ Propagating the flags via
``CFLAGS`` is how CPython starts that process.
``BASECFLAGS`` is how CPython starts that process.

Alternatives to Frame-Pointer Unwinding
---------------------------------------
Expand Down
Loading