https://github.com/python/cpython/commit/14f0b5191ad1d749d2ba5810f4f4495ee5581ce9
commit: 14f0b5191ad1d749d2ba5810f4f4495ee5581ce9
branch: main
author: Donghee Na <[email protected]>
committer: corona10 <[email protected]>
date: 2025-12-18T23:33:49+09:00
summary:

gh-142419: Add mmap.set_name method for user custom annotation (gh-142480)

files:
A Misc/NEWS.d/next/Library/2025-12-10-02-31-43.gh-issue-142419.C8_LES.rst
M Doc/library/mmap.rst
M Doc/whatsnew/3.15.rst
M Include/internal/pycore_mmap.h
M Lib/test/test_mmap.py
M Modules/clinic/mmapmodule.c.h
M Modules/mmapmodule.c
M Objects/obmalloc.c
M Python/jit.c
M Python/perf_jit_trampoline.c
M Python/perf_trampoline.c

diff --git a/Doc/library/mmap.rst b/Doc/library/mmap.rst
index f32aa322c40dbb..41b90f2c3b3111 100644
--- a/Doc/library/mmap.rst
+++ b/Doc/library/mmap.rst
@@ -328,6 +328,17 @@ To map anonymous memory, -1 should be passed as the fileno 
along with the length
 
       .. versionadded:: 3.13
 
+   .. method:: set_name(name, /)
+
+      Annotate the memory mapping with the given *name* for easier 
identification
+      in ``/proc/<pid>/maps`` if the kernel supports the feature and 
:option:`-X dev <-X>` is passed
+      to Python or if Python is built in :ref:`debug mode <debug-build>`.
+      The length of *name* must not exceed 67 bytes including the ``'\0'`` 
terminator.
+
+      .. availability:: Linux >= 5.17 (kernel built with 
``CONFIG_ANON_VMA_NAME`` option)
+
+      .. versionadded:: next
+
    .. method:: size()
 
       Return the length of the file, which can be larger than the size of the
diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index 753b1990eb3308..3c7e28a00c9c1a 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -592,6 +592,11 @@ mmap
   not be duplicated.
   (Contributed by Serhiy Storchaka in :gh:`78502`.)
 
+* Added the :meth:`mmap.mmap.set_name` method
+  to annotate an anonymous memory mapping
+  if Linux kernel supports :manpage:`PR_SET_VMA_ANON_NAME 
<PR_SET_VMA(2const)>` (Linux 5.17 or newer).
+  (Contributed by Donghee Na in :gh:`142419`.)
+
 
 os
 --
diff --git a/Include/internal/pycore_mmap.h b/Include/internal/pycore_mmap.h
index 214fd4362a55fe..897816db01077f 100644
--- a/Include/internal/pycore_mmap.h
+++ b/Include/internal/pycore_mmap.h
@@ -17,25 +17,27 @@ extern "C" {
 #endif
 
 #if defined(HAVE_PR_SET_VMA_ANON_NAME) && defined(__linux__)
-static inline void
+static inline int
 _PyAnnotateMemoryMap(void *addr, size_t size, const char *name)
 {
 #ifndef Py_DEBUG
     if (!_Py_GetConfig()->dev_mode) {
-        return;
+        return 0;
     }
 #endif
+    // The name length cannot exceed 80 (including the '\0').
     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;
+    int res = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, (unsigned long)addr, 
size, name);
+    if (res < 0) {
+        return -1;
+    }
+    return 0;
 }
 #else
-static inline void
+static inline int
 _PyAnnotateMemoryMap(void *Py_UNUSED(addr), size_t Py_UNUSED(size), const char 
*Py_UNUSED(name))
 {
+    return 0;
 }
 #endif
 
diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py
index 368af0cf89c300..aad916ecfe2c27 100644
--- a/Lib/test/test_mmap.py
+++ b/Lib/test/test_mmap.py
@@ -6,6 +6,7 @@
 from test.support.import_helper import import_module
 from test.support.os_helper import TESTFN, unlink
 from test.support.script_helper import assert_python_ok
+import errno
 import unittest
 import os
 import re
@@ -1165,6 +1166,46 @@ def test_flush_parameters(self):
             m.flush(PAGESIZE)
             m.flush(PAGESIZE, PAGESIZE)
 
+    @unittest.skipUnless(sys.platform == 'linux', 'Linux only')
+    @support.requires_linux_version(5, 17, 0)
+    def test_set_name(self):
+        # Test setting name on anonymous mmap
+        m = mmap.mmap(-1, PAGESIZE)
+        self.addCleanup(m.close)
+        try:
+            result = m.set_name('test_mapping')
+        except OSError as exc:
+            if exc.errno == errno.EINVAL:
+                # gh-142419: On Fedora, prctl(PR_SET_VMA_ANON_NAME) fails with
+                # EINVAL because the kernel option CONFIG_ANON_VMA_NAME is
+                # disabled.
+                # See: https://bugzilla.redhat.com/show_bug.cgi?id=2302746
+                self.skipTest("prctl() failed with EINVAL")
+            else:
+                raise
+        self.assertIsNone(result)
+
+        # Test name length limit (80 chars including prefix "cpython:mmap:" 
and '\0')
+        # Prefix is 13 chars, so max name is 66 chars
+        long_name = 'x' * 66
+        result = m.set_name(long_name)
+        self.assertIsNone(result)
+
+        # Test name too long
+        too_long_name = 'x' * 67
+        with self.assertRaises(ValueError):
+            m.set_name(too_long_name)
+
+        # Test that file-backed mmap raises error
+        with open(TESTFN, 'wb+') as f:
+            f.write(b'x' * PAGESIZE)
+            f.flush()
+            m2 = mmap.mmap(f.fileno(), PAGESIZE)
+            self.addCleanup(m2.close)
+
+            with self.assertRaises(ValueError):
+                m2.set_name('should_fail')
+
 
 class LargeMmapTests(unittest.TestCase):
 
diff --git 
a/Misc/NEWS.d/next/Library/2025-12-10-02-31-43.gh-issue-142419.C8_LES.rst 
b/Misc/NEWS.d/next/Library/2025-12-10-02-31-43.gh-issue-142419.C8_LES.rst
new file mode 100644
index 00000000000000..63955923cd157c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-12-10-02-31-43.gh-issue-142419.C8_LES.rst
@@ -0,0 +1,3 @@
+:meth:`mmap.mmap.set_name` method added to annotate an anonymous memory map
+if Linux kernel supports ``PR_SET_VMA_ANON_NAME`` (Linux 5.17 or newer).
+Patch by Donghee Na.
diff --git a/Modules/clinic/mmapmodule.c.h b/Modules/clinic/mmapmodule.c.h
index f7fc172b3af705..b63f7df2a7e334 100644
--- a/Modules/clinic/mmapmodule.c.h
+++ b/Modules/clinic/mmapmodule.c.h
@@ -479,6 +479,42 @@ mmap_mmap_seek(PyObject *self, PyObject *const *args, 
Py_ssize_t nargs)
     return return_value;
 }
 
+PyDoc_STRVAR(mmap_mmap_set_name__doc__,
+"set_name($self, name, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_SET_NAME_METHODDEF    \
+    {"set_name", (PyCFunction)mmap_mmap_set_name, METH_O, 
mmap_mmap_set_name__doc__},
+
+static PyObject *
+mmap_mmap_set_name_impl(mmap_object *self, const char *name);
+
+static PyObject *
+mmap_mmap_set_name(PyObject *self, PyObject *arg)
+{
+    PyObject *return_value = NULL;
+    const char *name;
+
+    if (!PyUnicode_Check(arg)) {
+        _PyArg_BadArgument("set_name", "argument", "str", arg);
+        goto exit;
+    }
+    Py_ssize_t name_length;
+    name = PyUnicode_AsUTF8AndSize(arg, &name_length);
+    if (name == NULL) {
+        goto exit;
+    }
+    if (strlen(name) != (size_t)name_length) {
+        PyErr_SetString(PyExc_ValueError, "embedded null character");
+        goto exit;
+    }
+    return_value = mmap_mmap_set_name_impl((mmap_object *)self, name);
+
+exit:
+    return return_value;
+}
+
 PyDoc_STRVAR(mmap_mmap_seekable__doc__,
 "seekable($self, /)\n"
 "--\n"
@@ -796,4 +832,4 @@ mmap_mmap_madvise(PyObject *self, PyObject *const *args, 
Py_ssize_t nargs)
 #ifndef MMAP_MMAP_MADVISE_METHODDEF
     #define MMAP_MMAP_MADVISE_METHODDEF
 #endif /* !defined(MMAP_MMAP_MADVISE_METHODDEF) */
-/*[clinic end generated code: output=381f6cf4986ac867 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=fd9ca0ef425af934 input=a9049054013a1b77]*/
diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c
index 37003020de2688..ea20fb29c90228 100644
--- a/Modules/mmapmodule.c
+++ b/Modules/mmapmodule.c
@@ -1117,6 +1117,47 @@ mmap_mmap_seek_impl(mmap_object *self, Py_ssize_t dist, 
int how)
     return NULL;
 }
 
+/*[clinic input]
+mmap.mmap.set_name
+
+    name: str
+    /
+
+[clinic start generated code]*/
+
+static PyObject *
+mmap_mmap_set_name_impl(mmap_object *self, const char *name)
+/*[clinic end generated code: output=1edaf4fd51277760 input=6c7dd91cad205f07]*/
+{
+#if defined(MAP_ANONYMOUS) && defined(__linux__)
+    const char *prefix = "cpython:mmap:";
+    if (strlen(name) + strlen(prefix) > 79) {
+        PyErr_SetString(PyExc_ValueError, "name is too long");
+        return NULL;
+    }
+    if (self->flags & MAP_ANONYMOUS) {
+        char buf[80];
+        sprintf(buf, "%s%s", prefix, name);
+        if (_PyAnnotateMemoryMap(self->data, self->size, buf) < 0) {
+            PyErr_SetFromErrno(PyExc_OSError);
+            return NULL;
+        }
+        Py_RETURN_NONE;
+    }
+    else {
+        /* cannot name non-anonymous mappings */
+        PyErr_SetString(PyExc_ValueError,
+                        "Cannot set annotation on non-anonymous mappings");
+        return NULL;
+    }
+#else
+    /* naming not supported on this platform */
+    PyErr_SetString(PyExc_NotImplementedError,
+                    "Annotation of mmap is not supported on this platform");
+    return NULL;
+#endif
+}
+
 /*[clinic input]
 mmap.mmap.seekable
 
@@ -1397,6 +1438,7 @@ static struct PyMethodDef mmap_object_methods[] = {
     MMAP_MMAP_RESIZE_METHODDEF
     MMAP_MMAP_SEEK_METHODDEF
     MMAP_MMAP_SEEKABLE_METHODDEF
+    MMAP_MMAP_SET_NAME_METHODDEF
     MMAP_MMAP_SIZE_METHODDEF
     MMAP_MMAP_TELL_METHODDEF
     MMAP_MMAP_WRITE_METHODDEF
@@ -1952,7 +1994,11 @@ new_mmap_object(PyTypeObject *type, PyObject *args, 
PyObject *kwdict)
         PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
-    _PyAnnotateMemoryMap(m_obj->data, map_size, "cpython:mmap");
+#ifdef MAP_ANONYMOUS
+    if (m_obj->flags & MAP_ANONYMOUS) {
+        (void)_PyAnnotateMemoryMap(m_obj->data, map_size, "cpython:mmap");
+    }
+#endif
     m_obj->access = (access_mode)access;
     return (PyObject *)m_obj;
 }
diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c
index b1f9fa2e692265..c4ccc9e283feb3 100644
--- a/Objects/obmalloc.c
+++ b/Objects/obmalloc.c
@@ -468,7 +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");
+    (void)_PyAnnotateMemoryMap(ptr, size, "cpython:pymalloc");
     return ptr;
 #else
     return malloc(size);
diff --git a/Python/jit.c b/Python/jit.c
index ccafe0ce497f43..7660f6f9beac89 100644
--- a/Python/jit.c
+++ b/Python/jit.c
@@ -77,7 +77,7 @@ jit_alloc(size_t size)
     unsigned char *memory = mmap(NULL, size, prot, flags, -1, 0);
     int failed = memory == MAP_FAILED;
     if (!failed) {
-        _PyAnnotateMemoryMap(memory, size, "cpython:jit");
+        (void)_PyAnnotateMemoryMap(memory, size, "cpython:jit");
     }
 #endif
     if (failed) {
diff --git a/Python/perf_jit_trampoline.c b/Python/perf_jit_trampoline.c
index af7d8f9f1ec0ae..0ffa906d85cc6b 100644
--- a/Python/perf_jit_trampoline.c
+++ b/Python/perf_jit_trampoline.c
@@ -1086,7 +1086,8 @@ 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");
+    (void)_PyAnnotateMemoryMap(perf_jit_map_state.mapped_buffer, page_size,
+                               "cpython:perf_jit_trampoline");
 #endif
 
     perf_jit_map_state.mapped_size = page_size;
diff --git a/Python/perf_trampoline.c b/Python/perf_trampoline.c
index 669a47ae17377a..335d8ac7dadd10 100644
--- a/Python/perf_trampoline.c
+++ b/Python/perf_trampoline.c
@@ -291,7 +291,7 @@ new_code_arena(void)
         perf_status = PERF_STATUS_FAILED;
         return -1;
     }
-    _PyAnnotateMemoryMap(memory, mem_size, "cpython:perf_trampoline");
+    (void)_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;

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to