---
LICENSE | 1 +
Makefile | 1 +
TODO | 2 +-
vmstat.c | 322 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 325 insertions(+), 1 deletion(-)
create mode 100644 vmstat.c
diff --git a/LICENSE b/LICENSE
index 76cf9ea..8b8e1ee 100644
--- a/LICENSE
+++ b/LICENSE
_AT_@ -32,3 +32,4 @@ Authors/contributors include:
© 2014 Roberto E. Vargas Caballero <k0ga_AT_shike2.com>
© 2014 Jan Tatje <jan_AT_jnt.io>
© 2015 Risto Salminen <ripejcp_AT_gmail.com>
+© 2019 Mattias Andrée <maandree_AT_kth.se>
diff --git a/Makefile b/Makefile
index b526421..e1dab9b 100644
--- a/Makefile
+++ b/Makefile
_AT_@ -86,6 +86,7 @@ BIN = \
umount \
unshare \
uptime \
+ vmstat \
vtallow \
watch \
who
diff --git a/TODO b/TODO
index 21f5c20..6dd69d3 100644
--- a/TODO
+++ b/TODO
_AT_@ -27,7 +27,7 @@ tabs
taskset
top
tput
-vmstat
+vmstat [-sdDpS]
Misc
====
diff --git a/vmstat.c b/vmstat.c
new file mode 100644
index 0000000..34208a4
--- /dev/null
+++ b/vmstat.c
_AT_@ -0,0 +1,322 @@
+/* See LICENSE file for copyright and license details. */
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "util.h"
+
+struct vm {
+ intmax_t cpu_user;
+ intmax_t cpu_nice;
+ intmax_t cpu_system;
+ intmax_t cpu_idle;
+ intmax_t cpu_iowait;
+ intmax_t cpu_irq;
+ intmax_t cpu_softirq;
+ intmax_t cpu_steal;
+ intmax_t cpu_guest;
+ intmax_t cpu_guest_nice;
+ intmax_t cpu_unknown;
+ intmax_t page_in;
+ intmax_t page_out;
+ intmax_t swap_in;
+ intmax_t swap_out;
+ intmax_t intr;
+ intmax_t ctxt_switches;
+ intmax_t processes;
+ intmax_t proc_running;
+ intmax_t proc_blocked;
+ intmax_t mem_free;
+ intmax_t buffers;
+ intmax_t cached;
+ intmax_t active;
+ intmax_t inactive;
+ intmax_t swap_total;
+ intmax_t swap_free;
+ intmax_t sreclaimable;
+};
+
+static intmax_t kb_per_page;
+static intmax_t hz;
+
+static int
+stpstarts(char *str, const char *head, char **end)
+{
+ size_t n = strlen(head);
+ if (!strncmp(str, head, n)) {
+ *end = &str[n];
+ return 1;
+ }
+ return 0;
+}
+
+static intmax_t
+read_ints(const char *s, intmax_t *arr, size_t n)
+{
+ const char *beginning = s;
+ intmax_t rest = 0;
+ size_t tmp;
+ int negative;
+
+ memset(arr, 0, n * sizeof(*arr));
+
+ for (; n--; arr++) {
+ while (*s && !isdigit(*s))
+ s++;
+ negative = (s != beginning && s[-1] == '-');
+ while (isdigit(*s))
+ *arr = *arr * 10 + (*s++ - '0');
+ if (negative)
+ *arr = -*arr;
+ }
+
+ for (; *s; rest += tmp) {
+ tmp = 0;
+ while (*s && !isdigit(*s))
+ s++;
+ negative = (s != beginning && s[-1] == '-');
+ while (isdigit(*s))
+ tmp = tmp * 10 + (*s++ - '0');
+ if (negative)
+ tmp = -tmp;
+ }
+
+ return rest;
+}
+
+static void
+load_vm(struct vm *s)
+{
+ static intmax_t debt = 0;
+
+ int have_page = 0, have_swap = 0;
+ char *line = NULL, *p;
+ size_t size = 0;
+ ssize_t len;
+ FILE *fp;
+
+ memset(s, 0, sizeof(*s));
+
+ fp = fopen("/proc/stat", "r");
+ if (!fp)
+ eprintf("fopen /proc/stat:");
+ while ((len = getline(&line, &size, fp)) >= 0) {
+ if (stpstarts(line, "cpu ", &p)) {
+ s->cpu_unknown = read_ints(p, &s->cpu_user, 10);
+ } else if (stpstarts(line, "page ", &p)) {
+ read_ints(p, &s->page_in, 2);
+ have_page = 1;
+ } else if (stpstarts(line, "swap ", &p)) {
+ read_ints(p, &s->swap_in, 2);
+ have_swap = 1;
+ } else if (stpstarts(line, "intr ", &p)) {
+ read_ints(p, &s->intr, 1);
+ } else if (stpstarts(line, "ctxt ", &p)) {
+ read_ints(p, &s->ctxt_switches, 1);
+ } else if (stpstarts(line, "processes ", &p)) {
+ read_ints(p, &s->processes, 1);
+ } else if (stpstarts(line, "proc_running ", &p)) {
+ read_ints(p, &s->proc_running, 1);
+ } else if (stpstarts(line, "proc_blocked ", &p)) {
+ read_ints(p, &s->proc_blocked, 1);
+ }
+ }
+ if (ferror(fp))
+ eprintf("getline /proc/stat:");
+ fclose(fp);
+
+ if (!have_page || !have_swap) {
+ fp = fopen("/proc/vmstat", "r");
+ if (!fp)
+ eprintf("fopen /proc/vmstat:");
+ while ((len = getline(&line, &size, fp)) >= 0) {
+ if (!have_page && stpstarts(line, "pgpgin ", &p))
+ read_ints(p, &s->page_in, 1);
+ else if (!have_page && stpstarts(line, "pgpgout ", &p))
+ read_ints(p, &s->page_out, 1);
+ else if (!have_swap && stpstarts(line, "pswpin ", &p))
+ read_ints(p, &s->swap_in, 1);
+ else if (!have_swap && stpstarts(line, "pswpout ", &p))
+ read_ints(p, &s->swap_out, 1);
+ }
+ if (ferror(fp))
+ eprintf("getline /proc/vmstat:");
+ fclose(fp);
+ }
+
+ fp = fopen("/proc/meminfo", "r");
+ if (!fp)
+ eprintf("fopen /proc/meminfo:");
+ while ((len = getline(&line, &size, fp)) >= 0) {
+ if (stpstarts(line, "MemFree:", &p))
+ read_ints(p, &s->mem_free, 1);
+ else if (stpstarts(line, "Buffers:", &p))
+ read_ints(p, &s->buffers, 1);
+ else if (stpstarts(line, "Cached:", &p))
+ read_ints(p, &s->cached, 1);
+ else if (stpstarts(line, "Active:", &p))
+ read_ints(p, &s->active, 1);
+ else if (stpstarts(line, "Inactive:", &p))
+ read_ints(p, &s->inactive, 1);
+ else if (stpstarts(line, "SwapTotal:", &p))
+ read_ints(p, &s->swap_total, 1);
+ else if (stpstarts(line, "SwapFree:", &p))
+ read_ints(p, &s->swap_free, 1);
+ else if (stpstarts(line, "SReclaimable:", &p))
+ read_ints(p, &s->sreclaimable, 1);
+ }
+ if (ferror(fp))
+ eprintf("getline /proc/meminfo:");
+ fclose(fp);
+
+ /* yes, this is actually needed */
+ s->cpu_idle += debt;
+ debt = 0;
+ if (s->cpu_idle < 0) {
+ debt = s->cpu_idle;
+ s->cpu_idle = 0;
+ }
+
+ free(line);
+}
+
+static void
+print_vm(struct vm *s1, struct vm *s0, int active_mem, int timestamp, int print_header)
+{
+ struct vm s = *s1;
+ intmax_t ticks;
+ char timezone[21];
+ char timestr[21];
+ struct tm *tm;
+ time_t now;
+ size_t n;
+
+ ticks = s.cpu_user -= s0->cpu_user;
+ ticks += s.cpu_nice -= s0->cpu_nice;
+ ticks += s.cpu_system -= s0->cpu_system;
+ ticks += s.cpu_idle -= s0->cpu_idle;
+ ticks += s.cpu_iowait -= s0->cpu_iowait;
+ ticks += s.cpu_irq -= s0->cpu_irq;
+ ticks += s.cpu_softirq -= s0->cpu_softirq;
+ ticks += s.cpu_steal -= s0->cpu_steal;
+ ticks += s.cpu_guest -= s0->cpu_guest;
+ ticks += s.cpu_guest_nice -= s0->cpu_guest_nice;
+ ticks += s.cpu_unknown -= s0->cpu_unknown;
+ s.processes -= s0->processes;
+ s.intr -= s0->intr;
+ s.ctxt_switches -= s0->ctxt_switches;
+ s.page_in -= s0->page_in;
+ s.page_out -= s0->page_out;
+
+ s.cpu_user += s.cpu_nice;
+ s.cpu_guest += s0->cpu_guest_nice;
+ s.cpu_idle += !ticks;
+ ticks += !ticks;
+
+ if (timestamp) {
+ now = time(NULL);
+ tm = localtime(&now);
+ strftime(timestr, sizeof(timestr), " %Y-%m-%d %H:%M:%S", tm);
+ strftime(timezone, sizeof(timezone), "%Z", tm);
+ n = strlen(timezone) + 1;
+ memmove(&timezone[sizeof(timezone) - n], timezone, n);
+ memset(timezone, ' ', sizeof(timezone) - n);
+ }
+
+#define PERCENT(X) ((X) * 100 + ticks / 2) / ticks
+#define HERTZ(X) ((X) * hz + ticks / 2) / ticks
+
+ if (!print_header)
+ goto print;
+ printf("----procs---- -------------------memory------------------ ---swap-- -----io---- --system- --------------cpu--------------%s\n",
+ timestamp ? " -----timestamp-----" : "");
+ printf(" r b fk swpd free " "%s " "%s si so bi bo in cs us sy id wa in si st gt%s\n",
+ active_mem ? "inact" : " buff", active_mem ? "active" : " cache", timestamp ? timezone : "");
+print:
+ printf("%2ji %2ji %7ji %10ji " "%10ji " "%10ji " "%10ji %4ji %4ji %5ji %5ji ""%4ji %4ji %3ji %3ji %3ji %3ji %3ji %3ji %3ji %3ji%s\n",
+ s.proc_running, s.proc_blocked, s.processes,
+ s.swap_total - s.swap_free, s.mem_free, active_mem ? s.inactive : s.buffers, active_mem ? s.active : s.cached + s.sreclaimable,
+ HERTZ(s.swap_in * kb_per_page), HERTZ(s.swap_out * kb_per_page),
+ HERTZ(s.page_in), HERTZ(s.page_out),
+ HERTZ(s.intr), HERTZ(s.ctxt_switches),
+ PERCENT(s.cpu_user), PERCENT(s.cpu_system), PERCENT(s.cpu_idle),
+ PERCENT(s.cpu_iowait), PERCENT(s.cpu_irq), PERCENT(s.cpu_softirq),
+ PERCENT(s.cpu_steal), PERCENT(s.cpu_guest),
+ timestamp ? timestr : "");
+
+#undef PERCENT
+#undef HERTZ
+}
+
+static void
+usage(void)
+{
+ eprintf("usage: %s [-ant] [delay [count]]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ static struct vm vm[2];
+ static struct timespec delay;
+ char *end;
+ double tmp;
+ unsigned long long int count = 0, i = 0;
+ int one_header = 0;
+ int active_mem = 0;
+ int timestamp = 0;
+
+ ARGBEGIN {
+ case 'a':
+ active_mem = 1;
+ break;
+ case 'n':
+ one_header = 1;
+ break;
+ case 't':
+ timestamp = 1;
+ break;
+ case 'w':
+ /* Ignored for compatibility (allow output to be wider than 80 columns) */
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if (argc > 2)
+ usage();
+
+ kb_per_page = (intmax_t)(sysconf(_SC_PAGESIZE) / 1024);
+ hz = (intmax_t)sysconf(_SC_CLK_TCK);
+
+ if (argc) {
+ errno = 0;
+ tmp = strtod(argv[0], &end);
+ if (errno || *end || tmp <= 0)
+ eprintf("%s: not a valid positive number\n", argv[0]);
+ delay.tv_sec = (time_t)tmp;
+ tmp = (tmp - (double)delay.tv_sec) * 1000000000.;
+ delay.tv_nsec = (long int)tmp;
+ if (delay.tv_nsec > 999999999L)
+ delay.tv_nsec = 999999999L;
+
+ if (argc > 1)
+ count = (unsigned long long int)atoll(argv[1]);
+ }
+
+ for (;;) {
+ load_vm(&vm[i & 1]);
+ print_vm(&vm[i & 1], &vm[~i & 1], active_mem, timestamp, one_header ? !i : (i % 50 == 0));
+ i++;
+ if (!argc || (argc == 2 && i == count))
+ break;
+ clock_nanosleep(CLOCK_MONOTONIC, 0, &delay, NULL);
+ }
+
+ return 0;
+}
--
2.23.0
Received on Tue Nov 05 2019 - 21:36:56 CET
This archive was generated by hypermail 2.3.0 : Tue Nov 05 2019 - 21:48:25 CET