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
615typedef 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+
687747static 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 },
0 commit comments