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
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."
#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;
}
+#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},
{"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},
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; }
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
[$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])