Skip to content

Commit f5e1df0

Browse files
committed
gh-150114: Log the memory usage in regrtest on FreeBSD
Add _testcapi.get_process_memory_usage(). On FreeBSD, _testcapi is now linked to libkvm.
1 parent a7d5a6c commit f5e1df0

4 files changed

Lines changed: 87 additions & 3 deletions

File tree

Lib/test/libregrtest/utils.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
import _winapi
2020
except ImportError:
2121
_winapi = None
22+
try:
23+
from _testcapi import get_process_memory_usage as _get_process_memory_usage
24+
except ImportError:
25+
_get_process_memory_usage = None
2226

2327
from test import support
2428
from test.support import os_helper
@@ -793,7 +797,9 @@ def _get_process_memory_usage_windows(pid: int) -> int | None:
793797
return mem_info['WorkingSetSize']
794798

795799

796-
if _winapi is not None:
800+
if _get_process_memory_usage is not None:
801+
get_process_memory_usage = _get_process_memory_usage
802+
elif _winapi is not None:
797803
get_process_memory_usage = _get_process_memory_usage_windows
798804
elif sys.platform == 'linux':
799805
get_process_memory_usage = _get_process_memory_usage_linux

Modules/_testcapi/mem.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
#include <stddef.h>
44

5+
#ifdef __FreeBSD__
6+
# include <fcntl.h> // O_RDONLY
7+
# include <kvm.h>
8+
# include <limits.h> // _POSIX2_LINE_MAX
9+
# include <sys/sysctl.h>
10+
# include <sys/types.h>
11+
# include <sys/user.h> // kinfo_proc definition
12+
# include <unistd.h> // sysconf()
13+
#endif
14+
515

616
typedef struct {
717
PyMemAllocatorEx alloc;
@@ -684,6 +694,57 @@ tracemalloc_track_race(PyObject *self, PyObject *args)
684694
}
685695

686696

697+
#ifdef __FreeBSD__
698+
// Return RSS only. Per-process swap usage isn't readily available
699+
static PyObject*
700+
get_process_memory_usage(PyObject *self, PyObject *args)
701+
{
702+
int pid;
703+
if (!PyArg_ParseTuple(args, "i", &pid)) {
704+
return NULL;
705+
}
706+
707+
long page_size = sysconf(_SC_PAGESIZE);
708+
if (page_size <= 0) {
709+
return PyErr_SetFromErrno(PyExc_OSError);
710+
}
711+
712+
// Using /dev/null for vmcore avoids needing dump file.
713+
// NULL for kernel file uses running kernel.
714+
char errbuf[_POSIX2_LINE_MAX];
715+
kvm_t *kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, errbuf);
716+
if (kd == NULL) {
717+
return PyErr_SetFromErrno(PyExc_OSError);
718+
}
719+
720+
// KERN_PROC_PID filters for the specific process ID.
721+
int n_procs;
722+
struct kinfo_proc *kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &n_procs);
723+
if (kp == NULL) {
724+
PyErr_SetFromErrno(PyExc_OSError);
725+
goto error;
726+
}
727+
if (n_procs <= 0) {
728+
// Process with PID not found
729+
errno = ESRCH;
730+
PyErr_SetFromErrno(PyExc_OSError);
731+
goto error;
732+
}
733+
assert(n_procs == 1);
734+
735+
// ki_rssize is in pages. Convert to bytes.
736+
size_t rss = (size_t)kp[0].ki_rssize * page_size;
737+
kvm_close(kd);
738+
739+
return PyLong_FromSize_t(rss);
740+
741+
error:
742+
kvm_close(kd);
743+
return NULL;
744+
}
745+
#endif
746+
747+
687748
static PyMethodDef test_methods[] = {
688749
{"pymem_api_misuse", pymem_api_misuse, METH_NOARGS},
689750
{"pymem_buffer_overflow", pymem_buffer_overflow, METH_NOARGS},
@@ -698,6 +759,9 @@ static PyMethodDef test_methods[] = {
698759
{"test_pymem_setrawallocators", test_pymem_setrawallocators, METH_NOARGS},
699760
{"test_pyobject_new", test_pyobject_new, METH_NOARGS},
700761
{"test_pyobject_setallocators", test_pyobject_setallocators, METH_NOARGS},
762+
#ifdef __FreeBSD__
763+
{"get_process_memory_usage", get_process_memory_usage, METH_VARARGS},
764+
#endif
701765

702766
// Tracemalloc tests
703767
{"tracemalloc_track", tracemalloc_track, METH_VARARGS},

configure

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

configure.ac

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8377,10 +8377,15 @@ PY_STDLIB_MOD([_hashlib], [], [test "$ac_cv_working_openssl_hashlib" = yes],
83778377
[$OPENSSL_INCLUDES], [$OPENSSL_LDFLAGS $OPENSSL_LDFLAGS_RPATH $LIBCRYPTO_LIBS])
83788378

83798379
dnl test modules
8380+
AS_CASE([$ac_sys_system],
8381+
# On FreeBSD, _testcapi.get_process_memory_usage() calls kvm_openfiles()
8382+
# and so needs libkvm.
8383+
[FreeBSD*], [LIBKVM="-lkvm"]
8384+
)
83808385
PY_STDLIB_MOD([_testcapi],
83818386
[test "$TEST_MODULES" = yes],
83828387
dnl Modules/_testcapi needs -latomic for 32bit AIX build
8383-
[], [], [$LIBATOMIC])
8388+
[], [], [$LIBATOMIC $LIBKVM])
83848389
PY_STDLIB_MOD([_testclinic], [test "$TEST_MODULES" = yes])
83858390
PY_STDLIB_MOD([_testclinic_limited], [test "$TEST_MODULES" = yes])
83868391
PY_STDLIB_MOD([_testlimitedcapi], [test "$TEST_MODULES" = yes])

0 commit comments

Comments
 (0)