]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-141770: Annotate anonymous mmap usage if "-X dev" is used (gh-142079)
authorDonghee Na <donghee.na@python.org>
Mon, 8 Dec 2025 14:47:19 +0000 (23:47 +0900)
committerGitHub <noreply@github.com>
Mon, 8 Dec 2025 14:47:19 +0000 (14:47 +0000)
15 files changed:
Doc/whatsnew/3.15.rst
Include/internal/pycore_mmap.h [new file with mode: 0644]
Makefile.pre.in
Misc/NEWS.d/next/Core_and_Builtins/2025-11-29-18-14-28.gh-issue-141770.JURnvg.rst [new file with mode: 0644]
Modules/_ctypes/malloc_closure.c
Modules/mmapmodule.c
Objects/obmalloc.c
PCbuild/pythoncore.vcxproj
PCbuild/pythoncore.vcxproj.filters
Python/jit.c
Python/perf_jit_trampoline.c
Python/perf_trampoline.c
configure
configure.ac
pyconfig.h.in

index 1bd82545e588face006b74214720f2ae9bb61e6d..0c892e63393ad1676623f535e48aeb5d35eab99e 100644 (file)
@@ -1236,6 +1236,12 @@ Build changes
   modules that are missing or packaged separately.
   (Contributed by Stan Ulbrych and Petr Viktorin in :gh:`139707`.)
 
+* Annotating anonymous mmap usage is now supported if Linux kernel supports
+  :manpage:`PR_SET_VMA_ANON_NAME <PR_SET_VMA(2const)>` (Linux 5.17 or newer).
+  Annotations are visible in ``/proc/<pid>/maps`` if the kernel supports the feature
+  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`)
+
 
 Porting to Python 3.15
 ======================
diff --git a/Include/internal/pycore_mmap.h b/Include/internal/pycore_mmap.h
new file mode 100644 (file)
index 0000000..214fd43
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef Py_INTERNAL_MMAP_H
+#define Py_INTERNAL_MMAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef Py_BUILD_CORE
+#  error "this header requires Py_BUILD_CORE define"
+#endif
+
+#include "pycore_pystate.h"
+
+#if defined(HAVE_PR_SET_VMA_ANON_NAME) && defined(__linux__)
+#  include <linux/prctl.h>
+#  include <sys/prctl.h>
+#endif
+
+#if defined(HAVE_PR_SET_VMA_ANON_NAME) && defined(__linux__)
+static inline void
+_PyAnnotateMemoryMap(void *addr, size_t size, const char *name)
+{
+#ifndef Py_DEBUG
+    if (!_Py_GetConfig()->dev_mode) {
+        return;
+    }
+#endif
+    assert(strlen(name) < 80);
+    int old_errno = errno;
+    prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, (unsigned long)addr, size, name);
+    /* Ignore errno from prctl */
+    /* See: https://bugzilla.redhat.com/show_bug.cgi?id=2302746 */
+    errno = old_errno;
+}
+#else
+static inline void
+_PyAnnotateMemoryMap(void *Py_UNUSED(addr), size_t Py_UNUSED(size), const char *Py_UNUSED(name))
+{
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif  // !Py_INTERNAL_MMAP_H
index f3086ec1462b6b77a3955a72587699d30742ea39..2554114fff6d6c5f0fe2511882a62a0efe7a82bf 100644 (file)
@@ -1378,6 +1378,7 @@ PYTHON_HEADERS= \
                $(srcdir)/Include/internal/pycore_long.h \
                $(srcdir)/Include/internal/pycore_memoryobject.h \
                $(srcdir)/Include/internal/pycore_mimalloc.h \
+               $(srcdir)/Include/internal/pycore_mmap.h \
                $(srcdir)/Include/internal/pycore_modsupport.h \
                $(srcdir)/Include/internal/pycore_moduleobject.h \
                $(srcdir)/Include/internal/pycore_namespace.h \
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-29-18-14-28.gh-issue-141770.JURnvg.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-29-18-14-28.gh-issue-141770.JURnvg.rst
new file mode 100644 (file)
index 0000000..3a5c0fd
--- /dev/null
@@ -0,0 +1,2 @@
+Annotate anonymous mmap usage only when supported by the
+Linux kernel and if ``-X dev`` is used or Python is built in debug mode. Patch by Donghee Na.
index db405acf8727b558b0355f3d0eef3703d601a21a..62c7aa5d6affbfbcae939caf49aad4f3277557ca 100644 (file)
@@ -14,6 +14,7 @@
 #  endif
 #endif
 #include "ctypes.h"
+#include "pycore_mmap.h"          // _PyAnnotateMemoryMap()
 
 /* BLOCKSIZE can be adjusted.  Larger blocksize will take a larger memory
    overhead, but allocate less blocks from the system.  It may be that some
@@ -74,14 +75,16 @@ static void more_core(void)
     if (item == NULL)
         return;
 #else
+    size_t mem_size = count * sizeof(ITEM);
     item = (ITEM *)mmap(NULL,
-                        count * sizeof(ITEM),
+                        mem_size,
                         PROT_READ | PROT_WRITE | PROT_EXEC,
                         MAP_PRIVATE | MAP_ANONYMOUS,
                         -1,
                         0);
     if (item == (void *)MAP_FAILED)
         return;
+    _PyAnnotateMemoryMap(item, mem_size, "cpython:ctypes");
 #endif
 
 #ifdef MALLOC_CLOSURE_DEBUG
index ac8521f8aa9b6ef3f80b4c3cbfbbdf9bcd63ee59..37003020de26889824f391eb1cbf546978b18a09 100644 (file)
@@ -26,6 +26,7 @@
 #include "pycore_abstract.h"      // _Py_convert_optional_to_ssize_t()
 #include "pycore_bytesobject.h"   // _PyBytes_Find()
 #include "pycore_fileutils.h"     // _Py_stat_struct
+#include "pycore_mmap.h"          // _PyAnnotateMemoryMap()
 #include "pycore_weakref.h"       // FT_CLEAR_WEAKREFS()
 
 #include <stddef.h>               // offsetof()
@@ -1951,6 +1952,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
         PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
+    _PyAnnotateMemoryMap(m_obj->data, map_size, "cpython:mmap");
     m_obj->access = (access_mode)access;
     return (PyObject *)m_obj;
 }
index 2b95ebbf8e5ac06b2150ab8b3eb4c2a9e63a5c51..b1f9fa2e692265c0eb3580dd51489496100fb4d9 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "Python.h"
 #include "pycore_interp.h"        // _PyInterpreterState_HasFeature
+#include "pycore_mmap.h"          // _PyAnnotateMemoryMap()
 #include "pycore_object.h"        // _PyDebugAllocatorStats() definition
 #include "pycore_obmalloc.h"
 #include "pycore_obmalloc_init.h"
@@ -467,6 +468,7 @@ _PyMem_ArenaAlloc(void *Py_UNUSED(ctx), size_t size)
     if (ptr == MAP_FAILED)
         return NULL;
     assert(ptr != NULL);
+    _PyAnnotateMemoryMap(ptr, size, "cpython:pymalloc");
     return ptr;
 #else
     return malloc(size);
index 85363949c2344f106585692bade5d84ad04298ce..dcfb75ce162b2fe4e8d6ee65b1cff91412768f90 100644 (file)
     <ClInclude Include="..\Include\internal\pycore_llist.h" />
     <ClInclude Include="..\Include\internal\pycore_lock.h" />
     <ClInclude Include="..\Include\internal\pycore_long.h" />
+    <ClInclude Include="..\Include\internal\pycore_mmap.h" />
     <ClInclude Include="..\Include\internal\pycore_modsupport.h" />
     <ClInclude Include="..\Include\internal\pycore_moduleobject.h" />
     <ClInclude Include="..\Include\internal\pycore_namespace.h" />
index 17999690990fb9e0c3ab2bd8305792324c7b1a93..247f4b5a784f9c630c467e55a8ae316b2a579979 100644 (file)
     <ClInclude Include="..\Include\internal\pycore_long.h">
       <Filter>Include\internal</Filter>
     </ClInclude>
+    <ClInclude Include="..\Include\internal\pycore_mmap.h">
+      <Filter>Include\internal</Filter>
+    </ClInclude>
     <ClInclude Include="..\Include\internal\pycore_modsupport.h">
       <Filter>Include\internal</Filter>
     </ClInclude>
index 47d3d7a5d271808adc29cb1ba6206e72ea1030d3..7106db8a99a77a239becad0e511ea7d54c26ce28 100644 (file)
@@ -16,6 +16,7 @@
 #include "pycore_intrinsics.h"
 #include "pycore_list.h"
 #include "pycore_long.h"
+#include "pycore_mmap.h"
 #include "pycore_opcode_metadata.h"
 #include "pycore_opcode_utils.h"
 #include "pycore_optimizer.h"
@@ -75,6 +76,9 @@ jit_alloc(size_t size)
     int prot = PROT_READ | PROT_WRITE;
     unsigned char *memory = mmap(NULL, size, prot, flags, -1, 0);
     int failed = memory == MAP_FAILED;
+    if (!failed) {
+        _PyAnnotateMemoryMap(memory, size, "cpython:jit");
+    }
 #endif
     if (failed) {
         jit_error("unable to allocate memory");
index 8732be973616d47cf61a1a63283103201c31d1f1..af7d8f9f1ec0ae254c41c6339ba70ce6b8d478af 100644 (file)
@@ -61,6 +61,7 @@
 #include "pycore_ceval.h"         // _PyPerf_Callbacks
 #include "pycore_frame.h"
 #include "pycore_interp.h"
+#include "pycore_mmap.h"          // _PyAnnotateMemoryMap()
 #include "pycore_runtime.h"       // _PyRuntime
 
 #ifdef PY_HAVE_PERF_TRAMPOLINE
@@ -1085,6 +1086,7 @@ static void* perf_map_jit_init(void) {
         close(fd);
         return NULL;  // Memory mapping failed
     }
+    _PyAnnotateMemoryMap(perf_jit_map_state.mapped_buffer, page_size, "cpython:perf_jit_trampoline");
 #endif
 
     perf_jit_map_state.mapped_size = page_size;
index 987e8d2a11a659e8c45e452dae8086ee49734aba..669a47ae17377a40683ffb3b92ce8df50727c1dc 100644 (file)
@@ -132,6 +132,7 @@ any DWARF information available for them).
 #include "Python.h"
 #include "pycore_ceval.h"         // _PyPerf_Callbacks
 #include "pycore_interpframe.h"   // _PyFrame_GetCode()
+#include "pycore_mmap.h"          // _PyAnnotateMemoryMap()
 #include "pycore_runtime.h"       // _PyRuntime
 
 
@@ -290,6 +291,7 @@ new_code_arena(void)
         perf_status = PERF_STATUS_FAILED;
         return -1;
     }
+    _PyAnnotateMemoryMap(memory, mem_size, "cpython:perf_trampoline");
     void *start = &_Py_trampoline_func_start;
     void *end = &_Py_trampoline_func_end;
     size_t code_size = end - start;
index 7561fb9c7ad90e40f6cb5dc7583a810a7f4c0dbe..4f9b9b21ca395e22a0be61cae341cad9c761d790 100755 (executable)
--- a/configure
+++ b/configure
@@ -23947,6 +23947,25 @@ printf "%s\n" "#define HAVE_UT_NAMESIZE 1" >>confdefs.h
 fi
 
 
+ac_fn_check_decl "$LINENO" "PR_SET_VMA_ANON_NAME" "ac_cv_have_decl_PR_SET_VMA_ANON_NAME" "#include <linux/prctl.h>
+               #include <sys/prctl.h>
+" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_PR_SET_VMA_ANON_NAME" = xyes
+then :
+  ac_have_decl=1
+else case e in #(
+  e) ac_have_decl=0 ;;
+esac
+fi
+printf "%s\n" "#define HAVE_DECL_PR_SET_VMA_ANON_NAME $ac_have_decl" >>confdefs.h
+if test $ac_have_decl = 1
+then :
+
+printf "%s\n" "#define HAVE_PR_SET_VMA_ANON_NAME 1" >>confdefs.h
+
+fi
+
+
 # check for openpty, login_tty, and forkpty
 
 
index fa24bc78a2645a997ec4230881d9f76fafd93f8d..046c046ffb5d10e0f9b141564b2054c3e99323fd 100644 (file)
@@ -5583,6 +5583,13 @@ AC_CHECK_DECLS([UT_NAMESIZE],
               [],
               [@%:@include <utmp.h>])
 
+AC_CHECK_DECLS([PR_SET_VMA_ANON_NAME],
+              [AC_DEFINE([HAVE_PR_SET_VMA_ANON_NAME], [1],
+                         [Define if you have the 'PR_SET_VMA_ANON_NAME' constant.])],
+              [],
+              [@%:@include <linux/prctl.h>
+               @%:@include <sys/prctl.h>])
+
 # check for openpty, login_tty, and forkpty
 
 AC_CHECK_FUNCS([openpty], [],
index 8a9f5ca8ec826de5a8755200afacb4e38c103612..aabf9f0be8da55eca59c523425c981345c9adbcd 100644 (file)
 /* Define to 1 if you have the <db.h> header file. */
 #undef HAVE_DB_H
 
+/* Define to 1 if you have the declaration of 'PR_SET_VMA_ANON_NAME', and to 0
+   if you don't. */
+#undef HAVE_DECL_PR_SET_VMA_ANON_NAME
+
 /* Define to 1 if you have the declaration of 'RTLD_DEEPBIND', and to 0 if you
    don't. */
 #undef HAVE_DECL_RTLD_DEEPBIND
 /* Define if your compiler supports function prototype */
 #undef HAVE_PROTOTYPES
 
+/* Define if you have the 'PR_SET_VMA_ANON_NAME' constant. */
+#undef HAVE_PR_SET_VMA_ANON_NAME
+
 /* Define to 1 if you have the 'pthread_condattr_setclock' function. */
 #undef HAVE_PTHREAD_CONDATTR_SETCLOCK