From f9d5b02e6645303b1eb04a96387924bfd2c0fe1c Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Fri, 27 Dec 2013 21:14:57 -0600 Subject: Cleanup vmstat. Procs, memory, and cpu are reading right, the others not so much. --- toys/other/vmstat.c | 279 +++++++++++++++++++++++----------------------------- 1 file changed, 124 insertions(+), 155 deletions(-) (limited to 'toys/other/vmstat.c') 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= 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 ' '); + 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; i5) 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; } } -- cgit v1.2.3