From cb3ab3782faaff9b78bcbdc2f47582c8f4ef1c0a Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 20 Dec 2023 16:42:41 +0100 Subject: [PATCH] [NetBSD] speedup net_connections() with proper filtering in C (#2342) Modify psutil.net_connections() to retrieving unnecessary connection types unless explicitly asked. E.g., on an IDLE system with few IPv6 connections this will run around 170% faster: ``` import psutil, time started = time.monotonic() for x in range(1000): psutil.net_connections("tcp6") print(f"completed in {(time.monotonic() - started):.4f} secs") ``` Before all connection types (TCP, UDP, UNIX) were retrived internally, even if they were not returned. --- HISTORY.rst | 14 +++ psutil/_psbsd.py | 12 ++- psutil/arch/netbsd/socks.c | 178 +++++++++++++++++-------------------- 3 files changed, 102 insertions(+), 102 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index c6ad7e144..932ac21b9 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,20 @@ 5.9.8 (IN DEVELOPMENT) ====================== +**Enhancements** + +- 2342_, [NetBSD]: filter `net_connections()`_ returned list in C instead of + Python, and avoid to retrieve unnecessary connection types unless explicitly + asked. E.g., on an IDLE system with few IPv6 connections this will run around + 170% faster. Before all connection types (TCP, UDP, UNIX) were retrived + internally, even if they were not returned.:: + + import psutil, time + started = time.monotonic() + for x in range(1000): + psutil.net_connections("tcp6") + print(f"completed in {(time.monotonic() - started):.4f} secs") + **Bug fixes** - 930_, [NetBSD], [critical]: `net_connections()`_ implementation was broken. diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index 6653dc9e1..04b615ff9 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -426,14 +426,13 @@ def net_connections(kind): if OPENBSD: rawlist = cext.net_connections(-1, families, types) elif NETBSD: - rawlist = cext.net_connections(-1) + rawlist = cext.net_connections(-1, kind) else: # FreeBSD rawlist = cext.net_connections() for item in rawlist: fd, fam, type, laddr, raddr, status, pid = item - if NETBSD or FREEBSD: - # OpenBSD implements filtering in C + if FREEBSD: if (fam not in families) or (type not in types): continue nt = conn_to_ntuple(fd, fam, type, laddr, raddr, @@ -786,16 +785,15 @@ def connections(self, kind='inet'): ret = [] if NETBSD: - rawlist = cext.net_connections(self.pid) + rawlist = cext.net_connections(self.pid, kind) elif OPENBSD: rawlist = cext.net_connections(self.pid, families, types) - else: # FreeBSD + else: rawlist = cext.proc_connections(self.pid, families, types) for item in rawlist: fd, fam, type, laddr, raddr, status = item[:6] - if NETBSD: - # FreeBSD and OpenBSD implement filtering in C + if FREEBSD: if (fam not in families) or (type not in types): continue nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, diff --git a/psutil/arch/netbsd/socks.c b/psutil/arch/netbsd/socks.c index 5c8ad3bd6..b3d6aac51 100644 --- a/psutil/arch/netbsd/socks.c +++ b/psutil/arch/netbsd/socks.c @@ -25,21 +25,6 @@ #include "../../_psutil_posix.h" -// address family filter -enum af_filter { - INET, - INET4, - INET6, - TCP, - TCP4, - TCP6, - UDP, - UDP4, - UDP6, - UNIX, - ALL, -}; - // kinfo_file results struct kif { SLIST_ENTRY(kif) kifs; @@ -245,84 +230,85 @@ psutil_get_sockets(const char *name) { // Collect open file and connections. static int -psutil_get_info(int aff) { - switch (aff) { - case INET: - if (psutil_get_sockets("net.inet.tcp.pcblist") != 0) - return -1; - if (psutil_get_sockets("net.inet.udp.pcblist") != 0) - return -1; - if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0) - return -1; - if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0) - return -1; - break; - case INET4: - if (psutil_get_sockets("net.inet.tcp.pcblist") != 0) - return -1; - if (psutil_get_sockets("net.inet.udp.pcblist") != 0) - return -1; - break; - case INET6: - if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0) - return -1; - if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0) - return -1; - break; - case TCP: - if (psutil_get_sockets("net.inet.tcp.pcblist") != 0) - return -1; - if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0) - return -1; - break; - case TCP4: - if (psutil_get_sockets("net.inet.tcp.pcblist") != 0) - return -1; - break; - case TCP6: - if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0) - return -1; - break; - case UDP: - if (psutil_get_sockets("net.inet.udp.pcblist") != 0) - return -1; - if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0) - return -1; - break; - case UDP4: - if (psutil_get_sockets("net.inet.udp.pcblist") != 0) - return -1; - break; - case UDP6: - if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0) - return -1; - break; - case UNIX: - if (psutil_get_sockets("net.local.stream.pcblist") != 0) - return -1; - if (psutil_get_sockets("net.local.seqpacket.pcblist") != 0) - return -1; - if (psutil_get_sockets("net.local.dgram.pcblist") != 0) - return -1; - break; - case ALL: - if (psutil_get_sockets("net.inet.tcp.pcblist") != 0) - return -1; - if (psutil_get_sockets("net.inet.udp.pcblist") != 0) - return -1; - if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0) - return -1; - if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0) - return -1; - if (psutil_get_sockets("net.local.stream.pcblist") != 0) - return -1; - if (psutil_get_sockets("net.local.seqpacket.pcblist") != 0) - return -1; - if (psutil_get_sockets("net.local.dgram.pcblist") != 0) - return -1; - break; +psutil_get_info(char *kind) { + if (strcmp(kind, "inet") == 0) { + if (psutil_get_sockets("net.inet.tcp.pcblist") != 0) + return -1; + if (psutil_get_sockets("net.inet.udp.pcblist") != 0) + return -1; + if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0) + return -1; + if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0) + return -1; + } + else if (strcmp(kind, "inet4") == 0) { + if (psutil_get_sockets("net.inet.tcp.pcblist") != 0) + return -1; + if (psutil_get_sockets("net.inet.udp.pcblist") != 0) + return -1; + } + else if (strcmp(kind, "inet6") == 0) { + if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0) + return -1; + if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0) + return -1; + } + else if (strcmp(kind, "tcp") == 0) { + if (psutil_get_sockets("net.inet.tcp.pcblist") != 0) + return -1; + if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0) + return -1; + } + else if (strcmp(kind, "tcp4") == 0) { + if (psutil_get_sockets("net.inet.tcp.pcblist") != 0) + return -1; + } + else if (strcmp(kind, "tcp6") == 0) { + if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0) + return -1; + } + else if (strcmp(kind, "udp") == 0) { + if (psutil_get_sockets("net.inet.udp.pcblist") != 0) + return -1; + if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0) + return -1; + } + else if (strcmp(kind, "udp4") == 0) { + if (psutil_get_sockets("net.inet.udp.pcblist") != 0) + return -1; + } + else if (strcmp(kind, "udp6") == 0) { + if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0) + return -1; + } + else if (strcmp(kind, "unix") == 0) { + if (psutil_get_sockets("net.local.stream.pcblist") != 0) + return -1; + if (psutil_get_sockets("net.local.seqpacket.pcblist") != 0) + return -1; + if (psutil_get_sockets("net.local.dgram.pcblist") != 0) + return -1; + } + else if (strcmp(kind, "all") == 0) { + if (psutil_get_sockets("net.inet.tcp.pcblist") != 0) + return -1; + if (psutil_get_sockets("net.inet.udp.pcblist") != 0) + return -1; + if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0) + return -1; + if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0) + return -1; + if (psutil_get_sockets("net.local.stream.pcblist") != 0) + return -1; + if (psutil_get_sockets("net.local.seqpacket.pcblist") != 0) + return -1; + if (psutil_get_sockets("net.local.dgram.pcblist") != 0) + return -1; + } + else { + PyErr_SetString(PyExc_ValueError, "invalid kind value"); + return -1; } - return 0; } @@ -334,6 +320,7 @@ PyObject * psutil_net_connections(PyObject *self, PyObject *args) { char laddr[PATH_MAX]; char raddr[PATH_MAX]; + char *kind; int32_t lport; int32_t rport; int32_t status; @@ -346,15 +333,16 @@ psutil_net_connections(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) - return NULL; + if (! PyArg_ParseTuple(args, _Py_PARSE_PID "s", &pid, &kind)) { + goto error; + } psutil_kiflist_init(); psutil_kpcblist_init(); if (psutil_get_files() != 0) goto error; - if (psutil_get_info(ALL) != 0) + if (psutil_get_info(kind) != 0) goto error; struct kif *k;