Skip to content

Commit dfe7ef6

Browse files
authored
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.
1 parent 46e8f7a commit dfe7ef6

4 files changed

Lines changed: 91 additions & 6 deletions

File tree

Lib/test/libregrtest/utils.py

Lines changed: 12 additions & 4 deletions
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,13 +797,17 @@ 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+
def get_process_memory_usage(pid: int) -> int | None:
802+
try:
803+
return _get_process_memory_usage(pid)
804+
except ProcessLookupError:
805+
return None
806+
elif _winapi is not None:
797807
get_process_memory_usage = _get_process_memory_usage_windows
798808
elif sys.platform == 'linux':
799809
get_process_memory_usage = _get_process_memory_usage_linux
800810
else:
801811
def get_process_memory_usage(pid: int) -> int | None:
802-
"""
803-
Get process memory usage in bytes.
804-
"""
805812
return None
813+
get_process_memory_usage.__doc__ = "Get process memory usage in bytes."

Modules/_testcapi/mem.c

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

33
#include <stddef.h>
44

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

615
typedef struct {
716
PyMemAllocatorEx alloc;
@@ -684,6 +693,57 @@ tracemalloc_track_race(PyObject *self, PyObject *args)
684693
}
685694

686695

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

702765
// Tracemalloc tests
703766
{"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
@@ -8428,10 +8428,15 @@ PY_STDLIB_MOD([_hashlib], [], [test "$ac_cv_working_openssl_hashlib" = yes],
84288428
[$OPENSSL_INCLUDES], [$OPENSSL_LDFLAGS $OPENSSL_LDFLAGS_RPATH $LIBCRYPTO_LIBS])
84298429

84308430
dnl test modules
8431+
AS_CASE([$ac_sys_system],
8432+
# On FreeBSD, _testcapi.get_process_memory_usage() calls kvm_openfiles()
8433+
# and so needs libkvm.
8434+
[FreeBSD*], [LIBKVM="-lkvm"]
8435+
)
84318436
PY_STDLIB_MOD([_testcapi],
84328437
[test "$TEST_MODULES" = yes],
84338438
dnl Modules/_testcapi needs -latomic for 32bit AIX build
8434-
[], [], [$LIBATOMIC])
8439+
[], [], [$LIBATOMIC $LIBKVM])
84358440
PY_STDLIB_MOD([_testclinic], [test "$TEST_MODULES" = yes])
84368441
PY_STDLIB_MOD([_testclinic_limited], [test "$TEST_MODULES" = yes])
84378442
PY_STDLIB_MOD([_testlimitedcapi], [test "$TEST_MODULES" = yes])

0 commit comments

Comments
 (0)