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