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]