[hackers] [slstatus][PATCH] Get disk IO stats on Linux (read, write, %)

From: K. Eugene Carlson <kvngncrlsn_AT_gmail.com>
Date: Fri, 3 Sep 2021 01:09:02 +0900

Hello, all.

This patch would let slstatus report on disk IO on Linux systems: reads
from disk, writes to disk and IO percentage.

io_in and io_out reference io_get, which is a light modification of code
from swap.c, so the first part of iocheck.c is very similar to that.
io_perc gets IO wait tick statistics from /sys/block, excluding virtual
devices to make a proper calculation. (As a note for testing purposes,
htop currently double-counts IO involving nvme drives.)

Performance-wise, running all three functions at the same time seems to
have about the same CPU load as wifi_perc, netspeed_rx and netspeed_tx
together.

If there's any feedback (positive or negative), needed changes, etc.,
I'd be very happy to hear about it!

Thanks very much,

Gene Carlson

---
 Makefile             |   1 +
 README               |   1 +
 components/iocheck.c | 170 +++++++++++++++++++++++++++++++++++++++++++
 config.def.h         |   3 +
 slstatus.h           |   5 ++
 5 files changed, 180 insertions(+)
 create mode 100644 components/iocheck.c
diff --git a/Makefile b/Makefile
index 2f93b87..fd85ad7 100644
--- a/Makefile
+++ b/Makefile
_AT_@ -12,6 +12,7 @@ COM =\
 	components/disk\
 	components/entropy\
 	components/hostname\
+	components/iocheck\
 	components/ip\
 	components/kernel_release\
 	components/keyboard_indicators\
diff --git a/README b/README
index 86fe988..996c22e 100644
--- a/README
+++ b/README
_AT_@ -15,6 +15,7 @@ Features
 - Available entropy
 - Username/GID/UID
 - Hostname
+- Disk IO (read, write and percentage) (Linux only)
 - IP address (IPv4 and IPv6)
 - Kernel version
 - Keyboard indicators
diff --git a/components/iocheck.c b/components/iocheck.c
new file mode 100644
index 0000000..d3ff9d8
--- /dev/null
+++ b/components/iocheck.c
_AT_@ -0,0 +1,170 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+
+#include "../util.h"
+
+#if defined(__linux__)
+	static int
+	get_io(uintmax_t *s_in, uintmax_t *s_out)
+	{
+		FILE *fp;
+		struct {
+			const char *name;
+			const size_t len;
+			uintmax_t *var;
+		} ent[] = {
+			{ "pgpgin",  sizeof("pgpgin") - 1,  s_in  },
+			{ "pgpgout", sizeof("pgpgout") - 1, s_out },
+		};
+		size_t line_len = 0, i, left;
+		char *line = NULL;
+			
+		/* get number of fields we want to extract */
+		for (i = 0, left = 0; i < LEN(ent); i++) {
+			if (ent[i].var) {
+				left++;
+			}
+		}
+                
+		if (!(fp = fopen("/proc/vmstat", "r"))) {
+			warn("fopen '/proc/vmstat':");
+			return 1;
+		}
+
+		/* read file line by line and extract field information */
+		while (left > 0 && getline(&line, &line_len, fp) >= 0) {
+			for (i = 0; i < LEN(ent); i++) {
+				if (ent[i].var &&
+				!strncmp(line,ent[i].name, ent[i].len)) {
+					sscanf(line + ent[i].len + 1,
+						"%ju\n", ent[i].var);
+					left--;
+					break;
+				}
+			}
+		}
+		free(line);
+		if(ferror(fp)) {
+			warn("getline '/proc/vmstat':");
+			return 1;
+		}
+		
+		fclose(fp);
+		return 0;
+	}	
+		
+	const char *
+	io_in(void)
+	{
+		uintmax_t oldin;
+		static uintmax_t newin;
+
+		oldin = newin;
+
+		if (get_io(&newin, NULL)) {
+			return NULL;
+		}
+		if (oldin == 0) {
+			return NULL;
+		}
+		
+		return fmt_human((newin-oldin) * 1024, 1024);
+	}
+
+	const char *
+	io_out(void)
+	{
+		uintmax_t oldout;
+		static uintmax_t newout;
+
+		oldout = newout;
+
+		if (get_io(NULL, &newout)) {
+			return NULL;
+		}
+		if (oldout == 0) {
+			return NULL;
+		}
+		
+		return fmt_human((newout - oldout) * 1024, 1024);
+	}
+
+	const char *
+	io_perc(void)
+	{
+		struct dirent *dp;
+		DIR *bd;
+		uintmax_t oldwait;
+		static uintmax_t newwait;
+		extern const unsigned int interval;
+
+		oldwait = newwait;
+		
+		if (!(bd = opendir("/sys/block"))) {
+			warn("opendir '%s':", "/sys/block");
+			return NULL;
+		} 
+
+		newwait = 0;
+		/* get IO wait stats from the /sys/block directories */
+		while ((dp = readdir(bd))) {
+			if (strstr(dp->d_name, "loop") ||
+			    strstr(dp->d_name, "ram")) {
+			   	continue;
+			}
+			if (!strcmp(dp->d_name, ".") ||
+			    !strcmp(dp->d_name, "..")) {
+			    	continue;
+			}
+			/* virtual devices don't count */
+			char virtpath[50];
+			strcpy(virtpath, "/sys/devices/virtual/block/");
+			strcat(virtpath, dp->d_name);
+			if (access(virtpath, R_OK) == 0) {
+				continue;
+			}
+			char statpath[34];
+			strcpy(statpath, "/sys/block/");
+			strcat(statpath, dp->d_name);
+			strcat(statpath, "/stat");
+			uintmax_t partwait;
+			if (pscanf(statpath,
+				"%*d %*d %*d %*d %*d %*d %*d %*d %*d %ju %*d",
+	   			&partwait) != 1) {
+				continue;
+			}
+			newwait += partwait;
+		}
+		closedir(bd);
+		if (oldwait == 0 || newwait < oldwait) {
+			return NULL;
+		}
+		
+		return bprintf("%0.1f", 100 *
+		       (newwait - oldwait) / (float)interval);
+	}
+
+#else
+	const char *
+	io_in(void)
+	{
+		return NULL;
+	}
+
+	const char *
+	io_out(void)
+	{
+		return NULL;
+	}
+
+	const char *
+	io_perc(void)
+	{
+		return NULL;
+	}
+#endif
diff --git a/config.def.h b/config.def.h
index 93a875a..c321c50 100644
--- a/config.def.h
+++ b/config.def.h
_AT_@ -28,6 +28,9 @@ static const char unknown_str[] = "n/a";
  * entropy             available entropy               NULL
  * gid                 GID of current user             NULL
  * hostname            hostname                        NULL
+ * io_in               disk IO (read)                  NULL
+ * io_out              disk IO (write)                 NULL
+ * io_perc             disk IO (percentage)            NULL
  * ipv4                IPv4 address                    interface name (eth0)
  * ipv6                IPv6 address                    interface name (eth0)
  * kernel_release      `uname -r`                      NULL
diff --git a/slstatus.h b/slstatus.h
index b0f2564..3464f5b 100644
--- a/slstatus.h
+++ b/slstatus.h
_AT_@ -24,6 +24,11 @@ const char *entropy(void);
 /* hostname */
 const char *hostname(void);
 
+/* iocheck */
+const char *io_in(void);
+const char *io_out(void);
+const char *io_perc(void);
+
 /* ip */
 const char *ipv4(const char *interface);
 const char *ipv6(const char *interface);
-- 
2.33.0
Received on Thu Sep 02 2021 - 18:09:02 CEST

This archive was generated by hypermail 2.3.0 : Thu Sep 02 2021 - 21:24:29 CEST