Skip to content

Commit 3236773

Browse files
pablogsalsavannahostrowskihugovkemmatyping
authored
gh-149202: Implement PEP 831 – Frame Pointers Everywhere: Enabling System-Level Observability for Python (#149201)
Co-authored-by: Savannah Ostrowski <savannah@python.org> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Emma Smith <emma@emmatyping.dev>
1 parent 690e0de commit 3236773

7 files changed

Lines changed: 170 additions & 17 deletions

File tree

Doc/howto/perf_profiling.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,9 @@ Example, using the :mod:`sys` APIs in file :file:`example.py`:
217217
How to obtain the best results
218218
------------------------------
219219

220-
For best results, Python should be compiled with
221-
``CFLAGS="-fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"`` as this allows
220+
For best results, keep frame pointers enabled. On supported GCC-compatible
221+
toolchains, CPython builds itself with ``-fno-omit-frame-pointer`` and, when
222+
available, ``-mno-omit-leaf-frame-pointer`` by default. These flags allow
222223
profilers to unwind using only the frame pointer and not on DWARF debug
223224
information. This is because as the code that is interposed to allow ``perf``
224225
support is dynamically generated it doesn't have any DWARF debugging information

Doc/using/configure.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,24 @@ also be used to improve performance.
780780

781781
.. versionadded:: 3.14
782782

783+
.. option:: --without-frame-pointers
784+
785+
Disable frame pointers, which are enabled by default (see :pep:`831`).
786+
787+
By default, the build appends ``-fno-omit-frame-pointer`` (and
788+
``-mno-omit-leaf-frame-pointer`` when the compiler supports it) to
789+
``BASECFLAGS`` so profilers, debuggers, and system tracing tools
790+
(``perf``, ``eBPF``, ``dtrace``, ``gdb``) can walk the C call stack
791+
without DWARF metadata. The flags propagate to third-party C
792+
extensions through :mod:`sysconfig`. On compilers that do not
793+
understand them, the build silently skips them.
794+
795+
Downstream packagers and authors of native libraries built with
796+
custom build systems should set the same flags so the unwind chain
797+
stays unbroken across all native frames.
798+
799+
.. versionadded:: 3.15
800+
783801
.. option:: --without-mimalloc
784802

785803
Disable the fast :ref:`mimalloc <mimalloc>` allocator

Doc/whatsnew/3.15.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ Summary -- Release highlights
8686
* :pep:`782`: :ref:`A new PyBytesWriter C API to create a Python bytes object
8787
<whatsnew315-pybyteswriter>`
8888
* :pep:`803`: :ref:`Stable ABI for Free-Threaded Builds <whatsnew315-abi3t>`
89+
* :pep:`831`: :ref:`Frame pointers everywhere <whatsnew315-frame-pointers>`
8990
* :ref:`The JIT compiler has been significantly upgraded <whatsnew315-jit>`
9091
* :ref:`Improved error messages <whatsnew315-improved-error-messages>`
9192
* :ref:`The official Windows 64-bit binaries now use the tail-calling interpreter
@@ -2262,6 +2263,16 @@ Build changes
22622263
and :option:`-X dev <-X>` is passed to the Python or Python is built in :ref:`debug mode <debug-build>`.
22632264
(Contributed by Donghee Na in :gh:`141770`.)
22642265

2266+
.. _whatsnew315-frame-pointers:
2267+
2268+
* CPython is now built with frame pointers enabled by default
2269+
(:pep:`831`). Pass :option:`--without-frame-pointers` to opt out.
2270+
Authors of C extensions and native libraries built with custom build
2271+
systems should add ``-fno-omit-frame-pointer`` and
2272+
``-mno-omit-leaf-frame-pointer`` to their own ``CFLAGS`` to keep the
2273+
unwind chain intact.
2274+
(Contributed by Pablo Galindo Salgado and Savannah Ostrowski in :gh:`149201`.)
2275+
22652276
.. _whatsnew315-windows-tail-calling-interpreter:
22662277

22672278
* 64-bit builds using Visual Studio 2026 (MSVC 18) may now use the new

Lib/test/test_frame_pointer_unwind.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,8 @@ def _frame_pointers_expected(machine):
2727
)
2828

2929
if "no-omit-frame-pointer" in cflags:
30-
# For example, configure adds -fno-omit-frame-pointer if Python
31-
# has perf trampoline (PY_HAVE_PERF_TRAMPOLINE) and Python is built
32-
# in debug mode.
30+
# For example, configure adds -fno-omit-frame-pointer by default on
31+
# supported GCC-compatible builds.
3332
return True
3433
if "omit-frame-pointer" in cflags:
3534
return False
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Enable frame pointers by default for GCC-compatible CPython builds, including
2+
``-mno-omit-leaf-frame-pointer`` when the compiler supports it, so profilers
3+
and debuggers can unwind native interpreter frames more reliably. Users can pass
4+
``--without-frame-pointers`` to opt out.

configure

Lines changed: 109 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

configure.ac

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2529,7 +2529,30 @@ then
25292529
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])
25302530
fi
25312531

2532+
AC_MSG_CHECKING([whether to build with frame pointers])
2533+
AC_ARG_WITH([frame-pointers],
2534+
[AS_HELP_STRING([--without-frame-pointers],
2535+
[build without frame pointers (default is no)])],
2536+
[],
2537+
[with_frame_pointers=yes])
2538+
AC_MSG_RESULT([$with_frame_pointers])
2539+
25322540
AS_VAR_IF([ac_cv_gcc_compat], [yes], [
2541+
dnl Keep frame pointers in CPython, stdlib objects, and third-party
2542+
dnl extensions built against this Python (BASECFLAGS propagates via
2543+
dnl sysconfig) so native profilers can unwind interpreter frames and
2544+
dnl generated trampolines without DWARF.
2545+
frame_pointer_cflags=
2546+
AX_CHECK_COMPILE_FLAG([-fno-omit-frame-pointer], [
2547+
frame_pointer_cflags="-fno-omit-frame-pointer"
2548+
AX_CHECK_COMPILE_FLAG([-mno-omit-leaf-frame-pointer], [
2549+
frame_pointer_cflags="$frame_pointer_cflags -mno-omit-leaf-frame-pointer"
2550+
], [], [-Werror])
2551+
], [], [-Werror])
2552+
if test -n "$frame_pointer_cflags" && test "x$with_frame_pointers" != xno; then
2553+
BASECFLAGS="$frame_pointer_cflags $BASECFLAGS"
2554+
fi
2555+
25332556
CFLAGS_NODIST="$CFLAGS_NODIST -std=c11"
25342557
25352558
PY_CHECK_CC_WARNING([enable], [extra], [if we can add -Wextra])
@@ -3788,11 +3811,6 @@ AC_MSG_RESULT([$perf_trampoline])
37883811
AS_VAR_IF([perf_trampoline], [yes], [
37893812
AC_DEFINE([PY_HAVE_PERF_TRAMPOLINE], [1], [Define to 1 if you have the perf trampoline.])
37903813
PERF_TRAMPOLINE_OBJ=Python/asm_trampoline.o
3791-
3792-
dnl perf needs frame pointers for unwinding, include compiler option in debug builds
3793-
AS_VAR_IF([Py_DEBUG], [true], [
3794-
AS_VAR_APPEND([BASECFLAGS], [" -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"])
3795-
])
37963814
])
37973815
AC_SUBST([PERF_TRAMPOLINE_OBJ])
37983816

0 commit comments

Comments
 (0)