https://github.com/python/cpython/commit/dfe7ef6292ec0618f0193dfe993683f46645e723
commit: dfe7ef6292ec0618f0193dfe993683f46645e723
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-05-25T13:45:55Z
summary:
gh-150114: Log the memory usage in regrtest on FreeBSD (#150280)
Add _testcapi.get_process_memory_usage().
On FreeBSD, _testcapi is now linked to libkvm.
files:
M Lib/test/libregrtest/utils.py
M Modules/_testcapi/mem.c
M configure
M configure.ac
diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py
index 21b84f7555b7713..b5b31bdce919285 100644
--- a/Lib/test/libregrtest/utils.py
+++ b/Lib/test/libregrtest/utils.py
@@ -19,6 +19,10 @@
import _winapi
except ImportError:
_winapi = None
+try:
+ from _testcapi import get_process_memory_usage as _get_process_memory_usage
+except ImportError:
+ _get_process_memory_usage = None
from test import support
from test.support import os_helper
@@ -793,13 +797,17 @@ def _get_process_memory_usage_windows(pid: int) -> int |
None:
return mem_info['WorkingSetSize']
-if _winapi is not None:
+if _get_process_memory_usage is not None:
+ def get_process_memory_usage(pid: int) -> int | None:
+ try:
+ return _get_process_memory_usage(pid)
+ except ProcessLookupError:
+ return None
+elif _winapi is not None:
get_process_memory_usage = _get_process_memory_usage_windows
elif sys.platform == 'linux':
get_process_memory_usage = _get_process_memory_usage_linux
else:
def get_process_memory_usage(pid: int) -> int | None:
- """
- Get process memory usage in bytes.
- """
return None
+get_process_memory_usage.__doc__ = "Get process memory usage in bytes."
diff --git a/Modules/_testcapi/mem.c b/Modules/_testcapi/mem.c
index b4896f984510bd6..f965b7cd390cd62 100644
--- a/Modules/_testcapi/mem.c
+++ b/Modules/_testcapi/mem.c
@@ -2,6 +2,15 @@
#include <stddef.h>
+#ifdef __FreeBSD__
+# include <fcntl.h> // O_RDONLY
+# include <kvm.h> // kvm_openfiles()
+# include <limits.h> // _POSIX2_LINE_MAX
+# include <sys/sysctl.h> // KERN_PROC_PID
+# include <sys/user.h> // kinfo_proc definition
+# include <unistd.h> // sysconf()
+#endif
+
typedef struct {
PyMemAllocatorEx alloc;
@@ -684,6 +693,57 @@ tracemalloc_track_race(PyObject *self, PyObject *args)
}
+#ifdef __FreeBSD__
+// Return RSS only. Per-process swap usage isn't readily available
+static PyObject*
+get_process_memory_usage(PyObject *self, PyObject *args)
+{
+ int pid;
+ if (!PyArg_ParseTuple(args, "i", &pid)) {
+ return NULL;
+ }
+
+ long page_size = sysconf(_SC_PAGESIZE);
+ if (page_size <= 0) {
+ return PyErr_SetFromErrno(PyExc_OSError);
+ }
+
+ // Using /dev/null for vmcore avoids needing dump file.
+ // NULL for kernel file uses running kernel.
+ char errbuf[_POSIX2_LINE_MAX];
+ kvm_t *kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, errbuf);
+ if (kd == NULL) {
+ return PyErr_SetFromErrno(PyExc_OSError);
+ }
+
+ // KERN_PROC_PID filters for the specific process ID.
+ int n_procs;
+ struct kinfo_proc *kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &n_procs);
+ if (kp == NULL) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+ if (n_procs <= 0) {
+ // Process with PID not found
+ errno = ESRCH;
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+ assert(n_procs == 1);
+
+ // ki_rssize is in pages. Convert to bytes.
+ size_t rss = (size_t)kp[0].ki_rssize * page_size;
+ kvm_close(kd);
+
+ return PyLong_FromSize_t(rss);
+
+error:
+ kvm_close(kd);
+ return NULL;
+}
+#endif
+
+
static PyMethodDef test_methods[] = {
{"pymem_api_misuse", pymem_api_misuse,
METH_NOARGS},
{"pymem_buffer_overflow", pymem_buffer_overflow,
METH_NOARGS},
@@ -698,6 +758,9 @@ static PyMethodDef test_methods[] = {
{"test_pymem_setrawallocators", test_pymem_setrawallocators,
METH_NOARGS},
{"test_pyobject_new", test_pyobject_new,
METH_NOARGS},
{"test_pyobject_setallocators", test_pyobject_setallocators,
METH_NOARGS},
+#ifdef __FreeBSD__
+ {"get_process_memory_usage", get_process_memory_usage,
METH_VARARGS},
+#endif
// Tracemalloc tests
{"tracemalloc_track", tracemalloc_track,
METH_VARARGS},
diff --git a/configure b/configure
index 1377b1eff4d2026..8135fd7d184c055 100755
--- a/configure
+++ b/configure
@@ -34499,6 +34499,15 @@ fi
printf "%s\n" "$py_cv_module__hashlib" >&6; }
+case $ac_sys_system in #(
+ # On FreeBSD, _testcapi.get_process_memory_usage() calls kvm_openfiles()
+ # and so needs libkvm.
+ FreeBSD*) :
+ LIBKVM="-lkvm"
+ ;; #(
+ *) :
+ ;;
+esac
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdlib extension
module _testcapi" >&5
printf %s "checking for stdlib extension module _testcapi... " >&6; }
@@ -34525,7 +34534,7 @@ fi
then :
- as_fn_append MODULE_BLOCK "MODULE__TESTCAPI_LDFLAGS=$LIBATOMIC$as_nl"
+ as_fn_append MODULE_BLOCK "MODULE__TESTCAPI_LDFLAGS=$LIBATOMIC
$LIBKVM$as_nl"
fi
if test "$py_cv_module__testcapi" = yes; then
diff --git a/configure.ac b/configure.ac
index 0c339c3c3a3a013..a84ac25c1c4c503 100644
--- a/configure.ac
+++ b/configure.ac
@@ -8428,10 +8428,15 @@ PY_STDLIB_MOD([_hashlib], [], [test
"$ac_cv_working_openssl_hashlib" = yes],
[$OPENSSL_INCLUDES], [$OPENSSL_LDFLAGS $OPENSSL_LDFLAGS_RPATH
$LIBCRYPTO_LIBS])
dnl test modules
+AS_CASE([$ac_sys_system],
+ # On FreeBSD, _testcapi.get_process_memory_usage() calls kvm_openfiles()
+ # and so needs libkvm.
+ [FreeBSD*], [LIBKVM="-lkvm"]
+)
PY_STDLIB_MOD([_testcapi],
[test "$TEST_MODULES" = yes],
dnl Modules/_testcapi needs -latomic for 32bit AIX build
- [], [], [$LIBATOMIC])
+ [], [], [$LIBATOMIC $LIBKVM])
PY_STDLIB_MOD([_testclinic], [test "$TEST_MODULES" = yes])
PY_STDLIB_MOD([_testclinic_limited], [test "$TEST_MODULES" = yes])
PY_STDLIB_MOD([_testlimitedcapi], [test "$TEST_MODULES" = yes])
_______________________________________________
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]