Skip to content
Open
Show file tree
Hide file tree
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
11 changes: 11 additions & 0 deletions Doc/library/mmap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

.. 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
Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,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
--
Expand Down
30 changes: 30 additions & 0 deletions Lib/test/test_mmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,36 @@ 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)
result = m.set_name('test_mapping')
self.assertIsNone(result)

# Test name length limit (80 chars including prefix "cpython:mmap:")
# Prefix is 13 chars, so max name is 67 chars
long_name = 'x' * 67
result = m.set_name(long_name)
self.assertIsNone(result)

# Test name too long
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test with NUL bytes inside it?

too_long_name = 'x' * 68
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):

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
38 changes: 37 additions & 1 deletion Modules/clinic/mmapmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 46 additions & 1 deletion Modules/mmapmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,46 @@ mmap_mmap_seek_impl(mmap_object *self, Py_ssize_t dist, int how)
return NULL;
}

/*clinic*/

/*[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) > 80) {
PyErr_SetString(PyExc_ValueError, "name is too long");
return NULL;
}
if (self->flags & MAP_ANONYMOUS) {
char buf[81];
sprintf(buf, "%s%s", prefix, name);
_PyAnnotateMemoryMap(self->data, self->size, buf);
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

Expand Down Expand Up @@ -1397,6 +1437,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
Expand Down Expand Up @@ -1952,7 +1993,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) {
_PyAnnotateMemoryMap(m_obj->data, map_size, "cpython:mmap");
}
#endif
m_obj->access = (access_mode)access;
return (PyObject *)m_obj;
}
Expand Down
Loading