vmstat.c revision c65c9cdc54c4ba51aeb34ab36b73286653013c8a
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
/* from UCB 5.4 5/17/86 */
/* from SunOS 4.1, SID 1.31 */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <unistd.h>
#include <memory.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <values.h>
#include <poll.h>
#include <locale.h>
#include "statcommon.h"
char *cmdname = "vmstat";
int caught_cont = 0;
static uint_t timestamp_fmt = NODATE;
static int hz;
static int pagesize;
static double etime;
static int lines = 1;
static int swflag = 0, cflag = 0, pflag = 0;
static int suppress_state;
static long iter = 0;
static hrtime_t period_n = 0;
static struct snapshot *ss;
struct iodev_filter df;
#define pgtok(a) ((a) * (pagesize >> 10))
#define denom(x) ((x) ? (x) : 1)
#define REPRINT 19
static void dovmstats(struct snapshot *old, struct snapshot *new);
static void printhdr(int);
static void dosum(struct sys_snapshot *ss);
static void dointr(struct snapshot *ss);
static void docachestats(kstat_ctl_t *kc, hrtime_t interval, int forever);
static void usage(void);
int
main(int argc, char **argv)
{
struct snapshot *old = NULL;
enum snapshot_types types = SNAP_SYSTEM;
int summary = 0;
int intr = 0;
kstat_ctl_t *kc;
int forever = 0;
hrtime_t start_n;
int c;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
#endif
(void) textdomain(TEXT_DOMAIN);
pagesize = sysconf(_SC_PAGESIZE);
hz = sysconf(_SC_CLK_TCK);
while ((c = getopt(argc, argv, "cipqsST:")) != EOF)
switch (c) {
case 'S':
swflag = !swflag;
break;
case 's':
summary = 1;
break;
case 'i':
intr = 1;
break;
case 'c':
cflag++;
break;
case 'q':
suppress_state = 1;
break;
case 'p':
pflag++; /* detailed paging info */
break;
case 'T':
if (optarg) {
if (*optarg == 'u')
timestamp_fmt = UDATE;
else if (*optarg == 'd')
timestamp_fmt = DDATE;
else
usage();
} else {
usage();
}
break;
default:
usage();
}
argc -= optind;
argv += optind;
/* consistency with iostat */
types |= SNAP_CPUS;
if (intr)
types |= SNAP_INTERRUPTS;
if (cflag)
types |= SNAP_FLUSHES;
if (!intr)
types |= SNAP_IODEVS;
/* max to fit in less than 80 characters */
df.if_max_iodevs = 4;
df.if_allowed_types = IODEV_DISK;
df.if_nr_names = 0;
df.if_names = safe_alloc(df.if_max_iodevs * sizeof (char *));
(void) memset(df.if_names, 0, df.if_max_iodevs * sizeof (char *));
while (argc > 0 && !isdigit(argv[0][0]) &&
df.if_nr_names < df.if_max_iodevs) {
df.if_names[df.if_nr_names] = *argv;
df.if_nr_names++;
argc--, argv++;
}
kc = open_kstat();
start_n = gethrtime();
ss = acquire_snapshot(kc, types, &df);
/* time, in seconds, since boot */
etime = ss->s_sys.ss_ticks / hz;
if (intr) {
dointr(ss);
free_snapshot(ss);
exit(0);
}
if (summary) {
dosum(&ss->s_sys);
free_snapshot(ss);
exit(0);
}
if (argc > 0) {
long interval;
char *endptr;
errno = 0;
interval = strtol(argv[0], &endptr, 10);
if (errno > 0 || *endptr != '\0' || interval <= 0 ||
interval > MAXINT)
usage();
period_n = (hrtime_t)interval * NANOSEC;
if (period_n <= 0)
usage();
iter = MAXLONG;
if (argc > 1) {
iter = strtol(argv[1], NULL, 10);
if (errno > 0 || *endptr != '\0' || iter <= 0)
usage();
} else
forever = 1;
if (argc > 2)
usage();
}
if (cflag) {
free_snapshot(ss);
docachestats(kc, period_n, forever);
exit(0);
}
(void) sigset(SIGCONT, printhdr);
dovmstats(old, ss);
while (forever || --iter > 0) {
/* (void) poll(NULL, 0, poll_interval); */
/* Have a kip */
sleep_until(&start_n, period_n, forever, &caught_cont);
free_snapshot(old);
old = ss;
ss = acquire_snapshot(kc, types, &df);
if (!suppress_state)
snapshot_report_changes(old, ss);
/* if config changed, show stats from boot */
if (snapshot_has_changed(old, ss)) {
free_snapshot(old);
old = NULL;
}
dovmstats(old, ss);
}
free_snapshot(old);
free_snapshot(ss);
free(df.if_names);
(void) kstat_close(kc);
return (0);
}
#define DELTA(v) (new->v - (old ? old->v : 0))
#define ADJ(n) ((adj <= 0) ? n : (adj >= n) ? 1 : n - adj)
#define adjprintf(fmt, n, val) adj -= (n + 1) - printf(fmt, ADJ(n), val)
static int adj; /* number of excess columns */
/*ARGSUSED*/
static void
show_disk(void *v1, void *v2, void *d)
{
struct iodev_snapshot *old = (struct iodev_snapshot *)v1;
struct iodev_snapshot *new = (struct iodev_snapshot *)v2;
hrtime_t oldtime = new->is_crtime;
double hr_etime;
double reads, writes;
if (new == NULL)
return;
if (old)
oldtime = old->is_stats.wlastupdate;
hr_etime = new->is_stats.wlastupdate - oldtime;
if (hr_etime == 0.0)
hr_etime = NANOSEC;
reads = new->is_stats.reads - (old ? old->is_stats.reads : 0);
writes = new->is_stats.writes - (old ? old->is_stats.writes : 0);
adjprintf(" %*.0f", 2, (reads + writes) / hr_etime * NANOSEC);
}
static void
dovmstats(struct snapshot *old, struct snapshot *new)
{
kstat_t *oldsys = NULL;
kstat_t *newsys = &new->s_sys.ss_agg_sys;
kstat_t *oldvm = NULL;
kstat_t *newvm = &new->s_sys.ss_agg_vm;
double percent_factor;
ulong_t sys_updates, vm_updates;
int count;
adj = 0;
if (old) {
oldsys = &old->s_sys.ss_agg_sys;
oldvm = &old->s_sys.ss_agg_vm;
}
etime = cpu_ticks_delta(oldsys, newsys);
percent_factor = 100.0 / denom(etime);
/*
* If any time has passed, convert etime to seconds per CPU
*/
etime = etime >= 1.0 ? (etime / nr_active_cpus(new)) / hz : 1.0;
sys_updates = denom(DELTA(s_sys.ss_sysinfo.updates));
vm_updates = denom(DELTA(s_sys.ss_vminfo.updates));
if (timestamp_fmt != NODATE) {
print_timestamp(timestamp_fmt);
lines--;
}
if (--lines <= 0)
printhdr(0);
adj = 0;
if (pflag) {
adjprintf(" %*u", 6,
pgtok((int)(DELTA(s_sys.ss_vminfo.swap_avail)
/ vm_updates)));
adjprintf(" %*u", 5,
pgtok((int)(DELTA(s_sys.ss_vminfo.freemem) / vm_updates)));
adjprintf(" %*.0f", 3, kstat_delta(oldvm, newvm, "pgrec")
/ etime);
adjprintf(" %*.0f", 3, (kstat_delta(oldvm, newvm, "hat_fault") +
kstat_delta(oldvm, newvm, "as_fault")) / etime);
adjprintf(" %*.0f", 3, pgtok(kstat_delta(oldvm, newvm, "dfree"))
/ etime);
adjprintf(" %*ld", 3, pgtok(new->s_sys.ss_deficit));
adjprintf(" %*.0f", 3, kstat_delta(oldvm, newvm, "scan")
/ etime);
adjprintf(" %*.0f", 4,
pgtok(kstat_delta(oldvm, newvm, "execpgin")) / etime);
adjprintf(" %*.0f", 4,
pgtok(kstat_delta(oldvm, newvm, "execpgout")) / etime);
adjprintf(" %*.0f", 4,
pgtok(kstat_delta(oldvm, newvm, "execfree")) / etime);
adjprintf(" %*.0f", 4,
pgtok(kstat_delta(oldvm, newvm, "anonpgin")) / etime);
adjprintf(" %*.0f", 4,
pgtok(kstat_delta(oldvm, newvm, "anonpgout")) / etime);
adjprintf(" %*.0f", 4,
pgtok(kstat_delta(oldvm, newvm, "anonfree")) / etime);
adjprintf(" %*.0f", 4,
pgtok(kstat_delta(oldvm, newvm, "fspgin")) / etime);
adjprintf(" %*.0f", 4,
pgtok(kstat_delta(oldvm, newvm, "fspgout")) / etime);
adjprintf(" %*.0f\n", 4,
pgtok(kstat_delta(oldvm, newvm, "fsfree")) / etime);
(void) fflush(stdout);
return;
}
adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.runque) / sys_updates);
adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.waiting) / sys_updates);
adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.swpque) / sys_updates);
adjprintf(" %*u", 6, pgtok((int)(DELTA(s_sys.ss_vminfo.swap_avail)
/ vm_updates)));
adjprintf(" %*u", 5, pgtok((int)(DELTA(s_sys.ss_vminfo.freemem)
/ vm_updates)));
adjprintf(" %*.0f", 3, swflag?
kstat_delta(oldvm, newvm, "swapin") / etime :
kstat_delta(oldvm, newvm, "pgrec") / etime);
adjprintf(" %*.0f", 3, swflag?
kstat_delta(oldvm, newvm, "swapout") / etime :
(kstat_delta(oldvm, newvm, "hat_fault")
+ kstat_delta(oldvm, newvm, "as_fault"))
/ etime);
adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "pgpgin"))
/ etime);
adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "pgpgout"))
/ etime);
adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "dfree"))
/ etime);
adjprintf(" %*ld", 2, pgtok(new->s_sys.ss_deficit));
adjprintf(" %*.0f", 2, kstat_delta(oldvm, newvm, "scan") / etime);
(void) snapshot_walk(SNAP_IODEVS, old, new, show_disk, NULL);
count = df.if_max_iodevs - new->s_nr_iodevs;
while (count-- > 0)
adjprintf(" %*d", 2, 0);
adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "intr") / etime);
adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "syscall") / etime);
adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "pswitch") / etime);
adjprintf(" %*.0f", 2,
kstat_delta(oldsys, newsys, "cpu_ticks_user") * percent_factor);
adjprintf(" %*.0f", 2, kstat_delta(oldsys, newsys, "cpu_ticks_kernel")
* percent_factor);
adjprintf(" %*.0f\n", 2, (kstat_delta(oldsys, newsys, "cpu_ticks_idle")
+ kstat_delta(oldsys, newsys, "cpu_ticks_wait"))
* percent_factor);
(void) fflush(stdout);
}
/*ARGSUSED*/
static void
print_disk(void *v, void *v2, void *d)
{
struct iodev_snapshot *iodev = (struct iodev_snapshot *)v2;
if (iodev == NULL)
return;
(void) printf("%c%c ", iodev->is_name[0], iodev->is_name[2]);
}
/* ARGSUSED */
static void
printhdr(int sig)
{
int i = df.if_max_iodevs - ss->s_nr_iodevs;
if (sig == SIGCONT)
caught_cont = 1;
if (pflag) {
(void) printf(" memory page ");
(void) printf("executable anonymous filesystem \n");
(void) printf(" swap free re mf fr de sr ");
(void) printf("epi epo epf api apo apf fpi fpo fpf\n");
lines = REPRINT;
return;
}
(void) printf(" kthr memory page ");
(void) printf("disk faults cpu\n");
if (swflag)
(void) printf(" r b w swap free si so pi po fr de sr ");
else
(void) printf(" r b w swap free re mf pi po fr de sr ");
(void) snapshot_walk(SNAP_IODEVS, NULL, ss, print_disk, NULL);
while (i-- > 0)
(void) printf("-- ");
(void) printf(" in sy cs us sy id\n");
lines = REPRINT;
}
static void
sum_out(char const *pretty, kstat_t *ks, char *name)
{
kstat_named_t *ksn = kstat_data_lookup(ks, name);
if (ksn == NULL) {
fail(0, "kstat_data_lookup('%s', '%s') failed",
ks->ks_name, name);
}
(void) printf("%9llu %s\n", ksn->value.ui64, pretty);
}
static void
dosum(struct sys_snapshot *ss)
{
uint64_t total_faults;
kstat_named_t *ksn;
long double nchtotal;
uint64_t nchhits;
sum_out("swap ins", &ss->ss_agg_vm, "swapin");
sum_out("swap outs", &ss->ss_agg_vm, "swapout");
sum_out("pages swapped in", &ss->ss_agg_vm, "pgswapin");
sum_out("pages swapped out", &ss->ss_agg_vm, "pgswapout");
ksn = kstat_data_lookup(&ss->ss_agg_vm, "hat_fault");
if (ksn == NULL) {
fail(0, "kstat_data_lookup('%s', 'hat_fault') failed",
ss->ss_agg_vm.ks_name);
}
total_faults = ksn->value.ui64;
ksn = kstat_data_lookup(&ss->ss_agg_vm, "as_fault");
if (ksn == NULL) {
fail(0, "kstat_data_lookup('%s', 'as_fault') failed",
ss->ss_agg_vm.ks_name);
}
total_faults += ksn->value.ui64;
(void) printf("%9llu total address trans. faults taken\n",
total_faults);
sum_out("page ins", &ss->ss_agg_vm, "pgin");
sum_out("page outs", &ss->ss_agg_vm, "pgout");
sum_out("pages paged in", &ss->ss_agg_vm, "pgpgin");
sum_out("pages paged out", &ss->ss_agg_vm, "pgpgout");
sum_out("total reclaims", &ss->ss_agg_vm, "pgrec");
sum_out("reclaims from free list", &ss->ss_agg_vm, "pgfrec");
sum_out("micro (hat) faults", &ss->ss_agg_vm, "hat_fault");
sum_out("minor (as) faults", &ss->ss_agg_vm, "as_fault");
sum_out("major faults", &ss->ss_agg_vm, "maj_fault");
sum_out("copy-on-write faults", &ss->ss_agg_vm, "cow_fault");
sum_out("zero fill page faults", &ss->ss_agg_vm, "zfod");
sum_out("pages examined by the clock daemon", &ss->ss_agg_vm, "scan");
sum_out("revolutions of the clock hand", &ss->ss_agg_vm, "rev");
sum_out("pages freed by the clock daemon", &ss->ss_agg_vm, "dfree");
sum_out("forks", &ss->ss_agg_sys, "sysfork");
sum_out("vforks", &ss->ss_agg_sys, "sysvfork");
sum_out("execs", &ss->ss_agg_sys, "sysexec");
sum_out("cpu context switches", &ss->ss_agg_sys, "pswitch");
sum_out("device interrupts", &ss->ss_agg_sys, "intr");
sum_out("traps", &ss->ss_agg_sys, "trap");
sum_out("system calls", &ss->ss_agg_sys, "syscall");
nchtotal = (long double) ss->ss_nc.ncs_hits.value.ui64 +
(long double) ss->ss_nc.ncs_misses.value.ui64;
nchhits = ss->ss_nc.ncs_hits.value.ui64;
(void) printf("%9.0Lf total name lookups (cache hits %.0Lf%%)\n",
nchtotal, nchhits / denom(nchtotal) * 100);
sum_out("user cpu", &ss->ss_agg_sys, "cpu_ticks_user");
sum_out("system cpu", &ss->ss_agg_sys, "cpu_ticks_kernel");
sum_out("idle cpu", &ss->ss_agg_sys, "cpu_ticks_idle");
sum_out("wait cpu", &ss->ss_agg_sys, "cpu_ticks_wait");
}
static void
dointr(struct snapshot *ss)
{
size_t i;
ulong_t total = 0;
(void) printf("interrupt total rate\n");
(void) printf("--------------------------------\n");
for (i = 0; i < ss->s_nr_intrs; i++) {
(void) printf("%-12.8s %10lu %8.0f\n",
ss->s_intrs[i].is_name, ss->s_intrs[i].is_total,
ss->s_intrs[i].is_total / etime);
total += ss->s_intrs[i].is_total;
}
(void) printf("--------------------------------\n");
(void) printf("Total %10lu %8.0f\n", total, total / etime);
}
static void
docachestats(kstat_ctl_t *kc, hrtime_t interval, int forever)
{
struct snapshot *old;
struct snapshot *new;
int i;
hrtime_t start;
start = gethrtime();
old = acquire_snapshot(kc, SNAP_FLUSHES, NULL);
if (iter == 0) {
(void) printf("flush statistics: (totals)\n");
(void) printf("%8s%8s%8s%8s%8s%8s\n",
"usr", "ctx", "rgn", "seg", "pag", "par");
(void) printf(" %7d %7d %7d %7d %7d %7d\n",
old->s_flushes.f_usr, old->s_flushes.f_ctx,
old->s_flushes.f_region, old->s_flushes.f_segment,
old->s_flushes.f_page, old->s_flushes.f_partial);
return;
}
(void) printf("flush statistics: (interval based)\n");
for (i = 0; i < iter; i++) {
if (i % REPRINT == 0)
(void) printf("%8s%8s%8s%8s%8s%8s\n",
"usr", "ctx", "rgn", "seg", "pag", "par");
/* Have a kip */
sleep_until(&start, interval, forever, &caught_cont);
new = acquire_snapshot(kc, SNAP_FLUSHES, NULL);
(void) printf(" %7d %7d %7d %7d %7d %7d\n",
new->s_flushes.f_usr - old->s_flushes.f_usr,
new->s_flushes.f_ctx - old->s_flushes.f_ctx,
new->s_flushes.f_region - old->s_flushes.f_region,
new->s_flushes.f_segment - old->s_flushes.f_segment,
new->s_flushes.f_page - old->s_flushes.f_page,
new->s_flushes.f_partial- old->s_flushes.f_partial);
(void) fflush(stdout);
free_snapshot(old);
old = new;
}
}
static void
usage(void)
{
(void) fprintf(stderr,
"Usage: vmstat [-cipqsS] [-T d|u] [disk ...] "
"[interval [count]]\n");
exit(1);
}