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.
This commit is contained in:
Victor Stinner 2026-05-25 15:45:55 +02:00 committed by GitHub
parent 46e8f7a9e7
commit dfe7ef6292
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 91 additions and 6 deletions

View file

@ -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."

View file

@ -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},

11
configure generated vendored
View file

@ -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

View file

@ -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])