aboutsummaryrefslogtreecommitdiff
path: root/toys/other/vmstat.c
diff options
context:
space:
mode:
Diffstat (limited to 'toys/other/vmstat.c')
-rw-r--r--toys/other/vmstat.c279
1 files changed, 124 insertions, 155 deletions
diff --git a/toys/other/vmstat.c b/toys/other/vmstat.c
index 16d45f2a..49c7cc72 100644
--- a/toys/other/vmstat.c
+++ b/toys/other/vmstat.c
@@ -8,177 +8,146 @@ config VMSTAT
bool "vmstat"
default y
help
- usage: vmstat [-n] [delay [count]]
- -n Display the header only once
- delay The delay between updates in seconds, when not specified
- the average since boot is displayed.
- count Number of updates to display, the default is inifinite.
-*/
-
-#include "toys.h"
-
-void read_proc_stat(unsigned int *proc_running, unsigned int *proc_blocked,
- uint64_t *sys_irq, uint64_t *sys_ctxt, uint64_t *cpu_user, uint64_t *cpu_sys,
- uint64_t *cpu_idle, uint64_t *cpu_wait)
-{
- char * off;
- uint64_t c_user, c_nice, c_sys, c_irq, c_sirq;
- int fd = xopen("/proc/stat", O_RDONLY);
- size_t s = xread(fd, toybuf, sizeof(toybuf)-1);
-
- toybuf[s] = 0;
- if (s == sizeof(toybuf)-1) error_exit("/proc/stat is too large");
-
- off = strstr(toybuf, "cpu ");
- // Ignoring steal and guest fields for now.
- if (off) sscanf(off, "cpu %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64 \
- " %"PRIu64" %"PRIu64" %"PRIu64, &c_user, &c_nice, &c_sys, cpu_idle,
- cpu_wait, &c_irq, &c_sirq);
- *cpu_user = c_user + c_nice;
- *cpu_sys = c_sys + c_irq + c_sirq;
- off = strstr(toybuf, "intr");
- if (off) sscanf(off, "intr %"PRIu64, sys_irq);
-
- off = strstr(toybuf, "ctxt");
- if (off) sscanf(off, "ctxt %"PRIu64, sys_ctxt);
-
- off = strstr(toybuf, "procs_running");
- if (off) sscanf(off, "procs_running %u", proc_running);
- (*proc_running)--; // look, i'm invisible.
-
- off = strstr(toybuf, "procs_blocked");
- if (off) sscanf(off, "procs_blocked %u", proc_blocked);
-
- close(fd);
-}
-
-void read_proc_meminfo(unsigned long *mem_swapped, unsigned long *mem_free,
- unsigned long *mem_buff, unsigned long *mem_cache)
-{
- char * off;
- unsigned long swap_total, swap_free;
- int fd = xopen("/proc/meminfo", O_RDONLY);
- size_t s = xread(fd, toybuf, sizeof(toybuf)-1);
-
- toybuf[s] = 0;
- if (s == sizeof(toybuf)-1) error_exit("/proc/meminfo is too large");
-
- off = strstr(toybuf, "MemFree");
- if (off) sscanf(off, "MemFree: %lu kB", mem_free);
-
- off = strstr(toybuf, "Buffers");
- if (off) sscanf(off, "Buffers: %lu kB", mem_buff);
+ usage: vmstat [-n] [DELAY [COUNT]]
- off = strstr(toybuf, "Cached");
- if (off) sscanf(off, "Cached: %lu kB", mem_cache);
+ Print virtual memory statistics, repeating each DELAY seconds, COUNT times.
+ (With no DELAY, prints one line. With no COUNT, repeats until killed.)
- off = strstr(toybuf, "SwapFree");
- if (off) sscanf(off, "SwapFree: %lu kB", &swap_free);
+ Show processes running and blocked, kilobytes swapped, free, buffered, and
+ cached, kilobytes swapped in and out per second, file disk blocks input and
+ output per second, interrupts and context switches per second, percent
+ of CPU time spent running user code, system code, idle, and awaiting I/O.
+ First line is since system started, later lines are since last line.
- off = strstr(toybuf, "SwapTotal");
- if (off) sscanf(off, "SwapTotal: %lu kB", &swap_total);
- *mem_swapped = swap_total - swap_free;
+ -n Display the header only once
+*/
- close(fd);
-}
+#define FOR_vmstat
+#include "toys.h"
-void read_proc_vmstat(unsigned long *io_pages_in, unsigned long *io_pages_out,
- unsigned long *swap_bytes_in, unsigned long *swap_bytes_out)
+struct vmstat_proc {
+ // From /proc/stat (jiffies)
+ uint64_t user, nice, sys, idle, wait, irq, sirq, intr, ctxt, running, blocked;
+ // From /proc/meminfo (units are kb)
+ uint64_t memfree, buffers, cached, swapfree, swaptotal;
+ // From /proc/vmstat (units are pages)
+ uint64_t io_in, io_out, swap_in, swap_out;
+};
+
+// All the elements of vmstat_proc are the same size, so we can populate it as
+// a big array, then read the elements back out by name
+void get_vmstat_proc(struct vmstat_proc *vmstat_proc)
{
- char *off;
- unsigned long s_pages_in, s_pages_out;
- unsigned long pagesize_kb = sysconf(_SC_PAGESIZE) / 1024L;
- int fd = xopen("/proc/vmstat", O_RDONLY);
- size_t s = xread(fd, toybuf, sizeof(toybuf)-1);
-
- toybuf[s] = 0;
- if (s == sizeof(toybuf)-1) error_exit("/proc/vmstat is too large");
-
- off = strstr(toybuf, "pgpgin");
- if (off) sscanf(off, "pgpgin %lu", io_pages_in);
-
- off = strstr(toybuf, "pgpgout");
- if (off) sscanf(off, "pgpgout %lu", io_pages_out);
-
- off = strstr(toybuf, "pswpin");
- if (off) sscanf(off, "pswpin %lu", &s_pages_in);
- *swap_bytes_in = s_pages_in * pagesize_kb;
+ char *vmstuff[] = { "/proc/stat", "cpu ", 0, 0, 0, 0, 0, 0,
+ "intr ", "ctxt ", "procs_running ", "procs_blocked ", "/proc/meminfo",
+ "MemFree: ", "Buffers: ", "Cached: ", "SwapFree: ", "SwapTotal: ",
+ "/proc/vmstat", "pgpgin ", "pgpgout ", "pswpin ", "pswpout " };
+ uint64_t *new = (uint64_t *)vmstat_proc;
+ char *p = p, *name = name;
+ int i, j;
+
+ // We use vmstuff to fill out vmstat_proc as an array of uint64_t:
+ // Strings starting with / are the file to find next entries in
+ // Any other string is a key to search for, with decimal value right after
+ // 0 means parse another value on same line as last key
+
+ for (i = 0; i<sizeof(vmstuff)/sizeof(char *); i++) {
+ if (!vmstuff[i]) p++;
+ else if (*vmstuff[i] == '/') {
+ xreadfile(name = vmstuff[i], toybuf, sizeof(toybuf));
+
+ continue;
+ } else {
+ if (!(p = strstr(toybuf, vmstuff[i]))) goto error;
+ p += strlen(vmstuff[i]);
+ }
+ if (1 != sscanf(p, "%"PRIu64"%n", new++, &j)) goto error;
+ p += j;
+ }
- off = strstr(toybuf, "pswpout");
- if (off) sscanf(off, "pswpout %lu", &s_pages_out);
- *swap_bytes_out = s_pages_out * pagesize_kb;
+ return;
- close(fd);
+error:
+ error_exit("No %sin %s\n", vmstuff[i], name);
}
void vmstat_main(void)
{
- const char fmt[] = "%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u\n";
- unsigned int loop_num = 0, loop_max_num = 0, loop_delay = 0;
- unsigned int running = 0, blocked = 0;
- unsigned long mem_swap = 0, mem_free = 0, mem_buff = 0, mem_cache = 0;
- unsigned long io_pages_in[2], io_pages_out[2], swap_bytes_in[2], swap_bytes_out[2];
- uint64_t sys_irq[2], sys_ctxt[2], cpu_user[2], cpu_sys[2], cpu_idle[2], cpu_wait[2];
- int first_run = 1;
- int no_header = toys.optflags;
- unsigned num_rows = 22;
-
- if (toys.optc >= 1) loop_delay = atoi(toys.optargs[0]);
- if (toys.optc >= 2) loop_max_num = atoi(toys.optargs[1]);
-
- if (loop_max_num < 0 || loop_delay < 0) error_exit("Invalid arguments");
-
- while(1) {
- uint64_t total_jif;
- int idx = loop_num%2;
-
- if(first_run || (!(loop_num % num_rows) && !no_header)) {
- unsigned rows = 0, cols = 0;
- terminal_size(&cols, &rows);
- num_rows = (rows > 3)? rows - 3 : 22;
+ struct vmstat_proc top[2];
+ int i, loop_delay = 0, loop_max = 0;
+ unsigned loop, rows = (toys.optflags & FLAG_n) ? 0 : 25,
+ page_kb = sysconf(_SC_PAGESIZE)/1024;
+ char *headers="r\0b\0swpd\0free\0buff\0cache\0si\0so\0bi\0bo\0in\0cs\0us\0"
+ "sy\0id\0wa", lengths[] = {2,2,6,6,6,6,4,4,5,5,4,4,2,2,2,2};
+
+ memset(top, 0, sizeof(top));
+ if (toys.optc) loop_delay = atolx_range(toys.optargs[0], 0, INT_MAX);
+ if (toys.optc > 1) loop_max = atolx_range(toys.optargs[1], 1, INT_MAX) - 1;
+
+ for (loop = 0; loop <= loop_max; loop++) {
+ unsigned idx = loop&1, offset = 0, expected = 0;
+ uint64_t units, total_hz, *ptr = (uint64_t *)(top+idx),
+ *oldptr = (uint64_t *)(top+!idx);
+
+ // Print headers
+ if (rows>3 && !(loop % (rows-3))) {
+ if (isatty(1)) terminal_size(0, &rows);
+ else rows = 0;
+
printf("procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----\n");
- printf(" r b swpd free buff cache si so bi bo in cs us sy id wa\n");
+
+ for (i=0; i<sizeof(lengths); i++) {
+ printf(" %*s"+!i, lengths[i], headers);
+ headers += strlen(headers)+1;
+ }
+ xputc('\n');
}
- read_proc_stat(&running, &blocked, &sys_irq[idx], &sys_ctxt[idx], &cpu_user[idx],
- &cpu_sys[idx], &cpu_idle[idx], &cpu_wait[idx]);
- read_proc_meminfo(&mem_swap, &mem_free, &mem_buff, &mem_cache);
- read_proc_vmstat(&io_pages_in[idx], &io_pages_out[idx], &swap_bytes_in[idx], &swap_bytes_out[idx]);
-
- if (first_run) {
- struct sysinfo inf;
- sysinfo(&inf);
- first_run = 0;
- total_jif = cpu_user[idx] + cpu_idle[idx] + cpu_wait[idx];
- printf(fmt, running, blocked, mem_swap, mem_free, mem_buff, mem_cache,
- (unsigned) (swap_bytes_in[idx]/inf.uptime),
- (unsigned) (swap_bytes_out[idx]/inf.uptime),
- (unsigned) (io_pages_in[idx]/inf.uptime),
- (unsigned) (io_pages_out[idx]/inf.uptime),
- (unsigned) (sys_irq[idx]/inf.uptime),
- (unsigned) (sys_ctxt[idx]/inf.uptime),
- (unsigned) (100*cpu_user[idx]/total_jif),
- (unsigned) (100*cpu_sys[idx]/total_jif),
- (unsigned) (100*cpu_idle[idx]/total_jif),
- (unsigned) (100*cpu_wait[idx]/total_jif));
- }else{
- total_jif = cpu_user[idx] - cpu_user[!idx] + cpu_idle[idx] - cpu_idle[!idx] + cpu_wait[idx] - cpu_wait[!idx];
- printf(fmt, running, blocked, mem_swap, mem_free, mem_buff, mem_cache,
- (unsigned) ((swap_bytes_in[idx] - swap_bytes_in[!idx])/loop_delay),
- (unsigned) ((swap_bytes_out[idx] - swap_bytes_out[!idx])/loop_delay),
- (unsigned) ((io_pages_in[idx] - io_pages_in[!idx])/loop_delay),
- (unsigned) ((io_pages_out[idx] - io_pages_out[!idx])/loop_delay),
- (unsigned) ((sys_irq[idx] - sys_irq[!idx])/loop_delay),
- (unsigned) ((sys_ctxt[idx] - sys_ctxt[!idx])/loop_delay),
- (unsigned) (100*(cpu_user[idx] - cpu_user[!idx])/total_jif),
- (unsigned) (100*(cpu_sys[idx] - cpu_sys[!idx]) /total_jif),
- (unsigned) (100*(cpu_idle[idx] - cpu_idle[!idx])/total_jif),
- (unsigned) (100*(cpu_wait[idx] - cpu_wait[!idx])/total_jif));
+ // Read data and combine some fields we display as aggregates
+ get_vmstat_proc(top+idx);
+ top[idx].running--; // Don't include ourselves
+ top[idx].user += top[idx].nice;
+ top[idx].sys += top[idx].irq + top[idx].sirq;
+ top[idx].swaptotal -= top[idx].swapfree;
+
+ // Collect unit adjustments (outside the inner loop to save time)
+
+ if (!loop) {
+ char *s = toybuf;
+
+ xreadfile("/proc/uptime", toybuf, sizeof(toybuf)-1);
+ while (*(s++) > ' ');
+ sscanf(s, "%"PRIu64, &units);
+ } else units = loop_delay;
+
+ // add up user, sys, idle, and wait time used since last time
+ // (Already appended nice to user)
+ total_hz = 0;
+ for (i=0; i<4; i++) total_hz += ptr[i+!!i] - oldptr[i+!!i];
+
+ // Output values in order[]: running, blocked, swaptotal, memfree, buffers,
+ // cache, swap_in, swap_out, io_in, io_out, sirq, ctxt, user, sys, idle,wait
+
+ for (i=0; i<sizeof(lengths); i++) {
+ char order[] = {9, 10, 15, 11, 12, 13, 18, 19, 16, 17, 6, 8, 0, 2, 3, 4};
+ uint64_t out = ptr[order[i]];
+ int len;
+
+ // Adjust rate and units
+ if (i>5) out -= oldptr[order[i]];
+ if (order[i]<7) out = ((out*100) + (total_hz/2)) / total_hz;
+ else if (order[i]>15) out = ((out * page_kb)+(units-1))/units;
+ else if (order[i]<9) out = (out+(units-1)) / units;
+
+ // If a field was too big to fit in its slot, try to compensate later
+ expected += lengths[i] + !!i;
+ len = expected - offset - !!i;
+ if (len < 0) len = 0;
+ offset += printf(" %*"PRIu64+!i, len, out);
}
+ xputc('\n');
- loop_num++;
- if (loop_delay == 0 || (loop_max_num != 0 && loop_num >= loop_max_num))
- break;
- sleep(loop_delay);
+ if (loop_delay) sleep(loop_delay);
+ else break;
}
}