diff options
author | Miles Bader <miles@gnu.org> | 1996-07-26 02:49:26 +0000 |
---|---|---|
committer | Miles Bader <miles@gnu.org> | 1996-07-26 02:49:26 +0000 |
commit | c63de13c80cd9f003b1861c2f0d360fc5c9acad1 (patch) | |
tree | 797821e0da777bc42efc9ccb964c78eab19bb062 /utils | |
parent | fd2f59752aa7ff131109d063ab1639088ee50bd7 (diff) |
(options, main): -k/--kilobytes, -v/--pages, and -b/--bytes options added.
(fields):
New struct members initialized.
`size', `cache-hit-ratio', `swap-size', `swap-active', `swap-free', and
`swap-pagesize' added.
(val_t, enum val_display_type, enum field_change_type): New types.
(val_display_type_is_size, print_val, vm_state_refresh,
vm_state_get_field, get_vmstats_field, get_size, ensure_def_pager_info,
get_swap_size, get_swap_free, get_swap_page_size, get_swap_active):
New functions.
(struct field):
CHANGE_TYPE, DISP_TYPE, STANDARD, and COMPUTE fields added.
CUM field removed.
(struct vm_state): New type.
(main):
Changed greatly to use vm_state type & functions, use print_val, and
support CONST display types nicely.
(argp_program_version): Version changed to 1.1.
Diffstat (limited to 'utils')
-rw-r--r-- | utils/vmstat.c | 437 |
1 files changed, 354 insertions, 83 deletions
diff --git a/utils/vmstat.c b/utils/vmstat.c index d99efcc7..6bb90ad3 100644 --- a/utils/vmstat.c +++ b/utils/vmstat.c @@ -28,11 +28,107 @@ #include <mach.h> #include <mach/vm_statistics.h> +#include <mach/default_pager.h> #include <hurd.h> -char *argp_program_version = "vmstat 1.0 (GNU " HURD_RELEASE ")"; +char *argp_program_version = "vmstat 1.1 (GNU " HURD_RELEASE ")"; -struct field { +static const struct argp_option options[] = { + {"terse", 't', 0, 0, "Use short one-line output format", 1 }, + {"no-header", 'H', 0, 0, "Don't print a descriptive header line"}, + {"prefix", 'p', 0, 0, "Always display a description before stats"}, + {"no-prefix", 'P', 0, 0, "Never display a description before stats"}, + {"pages", 'v', 0, 0, "Display sizes in pages"}, + {"kilobytes", 'k', 0, 0, "Display sizes in 1024 byte blocks"}, + {"bytes", 'b', 0, 0, "Display sizes in bytes"}, + + /* A header for all the individual field options. */ + { 0,0,0,0, "Selecting which statistics to show:", 2}, + + {0} +}; +static const char *args_doc = "[PERIOD [COUNT [HEADER_INTERVAL]]]"; +static const char *doc = "If PERIOD is supplied, then terse mode is" +" selected, and the output repeated every PERIOD seconds, with cumulative" +" fields given the difference from the last output. If COUNT is given" +" and non-zero, only that many lines are output. HEADER_INTERVAL" +" defaults to 23, and if not zero, is the number of repeats after which a" +" blank line and the header will be reprinted (as well as the totals for" +" cumulative fields)."; + +/* We use this one type to represent all values printed by this program. It + should be signed, and hopefully large enough! */ +typedef ssize_t val_t; + +/* How to print a given number. */ +enum val_display_type +{ + ASIS, /* Always printed as-is. */ + COUNT, /* As-is. */ + PAGES, /* Divided by the page size. */ + KBYTES, /* Divided by 1024. */ + BYTES, /* As-is. */ + SIZES, /* Use the most convenient unit, with suffix. */ + PCENT, /* Append `%'. */ +}; + +/* Returns true if TYPE is a `size' type. */ +static int +val_display_type_is_size (enum val_display_type type) +{ + return type == PAGES || type == KBYTES || type == BYTES || type == SIZES; +} + +/* Print a number in a way described by HOW. PSIZE is the page size. FWIDTH + is the width of the field to print it in, right-justified. If SIGN is + true, the value is always printed with a sign, even if it's positive. */ +static void +print_val (val_t val, int fwidth, enum val_display_type how, int sign, + size_t psize) +{ + if (how == SIZES) + { + float fval = val; + char *units = " KMGT", *u = units; + + while (fval > 1024) + { + fval /= 1024; + u++; + } + + printf ((fval >= 1000 + ? (sign ? "%+*.0f%c" : "%*.0f%c") + : (sign ? "%+*.3g%c" : "%*.3g%c")), + fwidth - 1, fval, *u); + } + else + { + if (how == PAGES) + val /= psize; + else if (how == KBYTES) + val /= 1024; + else if (how == PCENT) + fwidth--; + + printf (sign ? "%+*d" : "%*d", fwidth, val); + if (how == PCENT) + putchar ('%'); + } +} + +/* How this field changes with time. */ +enum field_change_type +{ + VARY, /* Can go up or down. */ + CONST, /* Always the same. */ + CUMUL, /* Monotonic increasing. */ +}; + +struct vm_state; /* fwd */ + +struct field +{ /* Name of the field; used for the option name. */ char *name; @@ -42,69 +138,190 @@ struct field { /* Terse header used for the columnar style output. */ char *hdr; - /* True if this field is `cumulative', that is, monotonic increasing. */ - int cum; + /* Type of this field. */ + enum field_change_type change_type; + + /* How to display the number associated with this field. + If this is anything but `DIMLESS', then it can be overriden by the + user. */ + enum val_display_type disp_type; - /* Offset of the integer_t field in a vm_statistics structure */ + /* True if we display this field by default (user can always override). */ + int standard :1; + + /* Offset of the integer_t field in a vm_statistics structure. -1 if a + computed field (in which case the COMPUTE field should be filled in). */ int offs; + + /* How to compute this field. If 0, get_vmstats_value is used. This + function should return a negative number if there's some problem getting + the field. */ + val_t (*compute)(struct vm_state *state, const struct field *field); }; + +/* State about system vm from which we compute the above defined fields. */ +struct vm_state +{ + /* General vm statistics. */ + struct vm_statistics vmstats; -/* Returns the byte offset of the field FIELD in a vm_statistics structure. */ -#define FOFFS(field_name) offsetof (struct vm_statistics, field_name) + /* default pager port (must be privileged to fetch this). */ + mach_port_t def_pager; + struct default_pager_info def_pager_info; +}; -/* Yields an lvalue refering to FIELD in STATS. */ -#define FVAL(stats, field) (*(integer_t *)((char *)&(stats) + (field)->offs)) +static error_t +vm_state_refresh (struct vm_state *state) +{ + error_t err = vm_statistics (mach_task_self (), &state->vmstats); -/* vm_statistics fields we know about. */ -static const struct field fields[] = { - { "pagesize", "Pagesize", "pgsz", 0, FOFFS (pagesize) }, - { "free", "Free pages", "free", 0, FOFFS (free_count) }, - { "active", "Active pages", "actv", 0, FOFFS (active_count) }, - { "inactive", "Inactive pages", "inac", 0, FOFFS (inactive_count) }, - { "wired", "Wired pages", "wire", 0, FOFFS (wire_count) }, - { "zero-filled", "Zeroed pages", "zeroed", 1, FOFFS (zero_fill_count) }, - { "reactivations","Reactivations", "react", 1, FOFFS (reactivations) }, - { "pageins", "Pageins", "pgins", 1, FOFFS (pageins) }, - { "pageouts", "Pageouts", "pgouts", 1, FOFFS (pageouts) }, - { "faults", "Faults", "pfaults", 1, FOFFS (faults) }, - { "cow-faults", "Cow faults", "cowpfs", 1, FOFFS (cow_faults) }, - { "cache-lookups","Cache lookups", "clkups", 1, FOFFS (lookups) }, - { "cache-hits", "Cache hits", "chits", 1, FOFFS (hits) }, - {0} -}; + if (err) + return err; -static const struct argp_option options[] = { - {"terse", 't', 0, 0, "Use short one-line output format", 1 }, - {"no-header", 'H', 0, 0, "Don't print a descriptive header line"}, - {"prefix", 'p', 0, 0, "Always display a description before stats"}, - {"no-prefix", 'P', 0, 0, "Never display a description before stats"}, + /* Mark the info as invalid, but leave DEF_PAGER alone. */ + bzero (&state->def_pager_info, sizeof state->def_pager_info); - /* A header for all the individual field options. */ - { 0,0,0,0, "Selecting which statistics to show:", 2}, + return 0; +} + +static val_t +get_vmstats_field (struct vm_state *state, const struct field *field) +{ + val_t val = + (val_t)(*(integer_t *)((char *)&state->vmstats + field->offs)); + if (val_display_type_is_size (field->disp_type)) + val *= state->vmstats.pagesize; + return val; +} + +static val_t +get_size (struct vm_state *state, const struct field *field) +{ + return + (state->vmstats.free_count + state->vmstats.active_count + + state->vmstats.inactive_count + state->vmstats.wire_count) + * state->vmstats.pagesize; +} + +static val_t +vm_state_get_field (struct vm_state *state, const struct field *field) +{ + return (field->compute ?: get_vmstats_field) (state, field); +} + +static val_t +get_cache_hit_ratio (struct vm_state *state, const struct field *field) +{ + return state->vmstats.hits * 100 / state->vmstats.lookups; +} + +/* Makes sure STATE contains a default pager port and associated info, and + returns 0 if not (after printing an error). */ +static int +ensure_def_pager_info (struct vm_state *state) +{ + error_t err; + + if (state->def_pager == MACH_PORT_NULL) + { + mach_port_t host; + + err = get_privileged_ports (&host, 0); + if (err) + { + error (0, err, "get_privileged_ports"); + return 0; + } + + err = vm_set_default_memory_manager (host, &state->def_pager); + mach_port_deallocate (mach_task_self (), host); + + if (err) + { + error (0, err, "vm_set_default_memory_manager"); + return 0; + } + } + + err = default_pager_info (state->def_pager, &state->def_pager_info); + if (err) + error (0, err, "default_pager_info"); + + return (err == 0); +} +static val_t +get_swap_size (struct vm_state *state, const struct field *field) +{ + return + ensure_def_pager_info (state) ? state->def_pager_info.dpi_total_space : -1; +} + +static val_t +get_swap_free (struct vm_state *state, const struct field *field) +{ + return + ensure_def_pager_info (state) ? state->def_pager_info.dpi_free_space : -1; +} + +static val_t +get_swap_page_size (struct vm_state *state, const struct field *field) +{ + return + ensure_def_pager_info (state) ? state->def_pager_info.dpi_page_size : -1; +} + +static val_t +get_swap_active (struct vm_state *state, const struct field *field) +{ + return + ensure_def_pager_info (state) + ? (state->def_pager_info.dpi_total_space + - state->def_pager_info.dpi_free_space) + : -1; +} + +/* Returns the byte offset of the field FIELD in a vm_statistics structure. */ +#define _F(field_name) offsetof (struct vm_statistics, field_name) + +/* vm_statistics fields we know about. */ +static const struct field fields[] = +{ + {"pagesize", "Pagesize", " pgsz", CONST,ASIS, 1,_F(pagesize)}, + {"size", "Size", " size", CONST,SIZES,1,0,get_size}, + {"free", "Free", " free", VARY, SIZES,1,_F(free_count)}, + {"active", "Active", " actv", VARY, SIZES,1,_F(active_count)}, + {"inactive", "Inactive", "inact", VARY, SIZES,1,_F(inactive_count)}, + {"wired", "Wired", "wired", VARY, SIZES,1,_F(wire_count)}, + {"zero-filled", "Zeroed", "zeroed", CUMUL,SIZES,1,_F(zero_fill_count)}, + {"reactivated", "Reactivated", "react", CUMUL,SIZES,1,_F(reactivations)}, + {"pageins", "Pageins", "pgins", CUMUL,SIZES,1,_F(pageins)}, + {"pageouts", "Pageouts", "pgouts", CUMUL,SIZES,1,_F(pageouts)}, + {"faults", "Faults", "pfaults",CUMUL,COUNT,1,_F(faults)}, + {"cow-faults", "Cow faults", "cowpfs", CUMUL,COUNT,1,_F(cow_faults)}, + {"cache-lookups","Cache lookups","clkups", CUMUL,COUNT,0,_F(lookups)}, + {"cache-hits", "Cache hits", "chits", CUMUL,COUNT,0,_F(hits)}, + {"cache-hit-ratio","Cache hit ratio","chrat",VARY,PCENT,1,-1,get_cache_hit_ratio}, + {"swap-size", "Swap size", "swsize", CONST,SIZES,1,0,get_swap_size}, + {"swap-active", "Swap active", "swactv", VARY, SIZES,0,0,get_swap_active}, + {"swap-free", "Swap free", "swfree", VARY, SIZES,1,0,get_swap_free}, + {"swap-pagesize","Swap pagesize","swpgsz", CONST,ASIS, 0,0,get_swap_page_size}, {0} }; -static const char *args_doc = "[PERIOD [COUNT [HEADER_INTERVAL]]]"; -static const char *doc = "If PERIOD is supplied, then terse mode is" -" selected, and the output repeated every PERIOD seconds, with cumulative" -" fields given the difference from the last output. If COUNT is given" -" and non-zero, only that many lines are output. HEADER_INTERVAL" -" defaults to 23, and if not zero, is the number of repeats after which a" -" blank line and the header will be reprinted (as well as the totals for" -" cumulative fields)."; - +#undef _F + int main (int argc, char **argv) { error_t err; const struct field *field; - struct vm_statistics stats; + struct vm_state state; int num_fields = 0; /* Number of vm_fields known. */ unsigned long output_fields = 0; /* A bit per field, from 0. */ int count = 1; /* Number of repeats. */ unsigned period = 0; /* Seconds between terse mode repeats. */ - unsigned hdr_interval = 23; /* */ - + unsigned hdr_interval = 22; /* */ + enum val_display_type user_disp_type = ASIS; int terse = 0, print_heading = 1, print_prefix = -1; /* Parse our options... */ @@ -120,6 +337,9 @@ main (int argc, char **argv) case 'p': print_prefix = 1; break; case 'P': print_prefix = 0; break; case 'H': print_heading = 0; break; + case 'b': user_disp_type = BYTES; break; + case 'v': user_disp_type = PAGES; break; + case 'k': user_disp_type = KBYTES; break; case ARGP_KEY_ARG: terse = 1; @@ -174,17 +394,49 @@ main (int argc, char **argv) argp_parse (&argp, argc, argv, 0, 0, 0); if (output_fields == 0) - output_fields = ~0; /* By default, show all fields. */ + /* Show default fields. */ + for (field = fields; field->name; field++) + if (field->standard) + output_fields |= (1 << (field - fields)); + + /* Returns a value describing how to display FIELD. */ +#define FDISPTYPE(field) \ + (user_disp_type == ASIS || !val_display_type_is_size ((field)->disp_type) \ + ? (field)->disp_type \ + : user_disp_type) + + /* Prints SEP if the variable FIRST is 0, otherwise, prints START (if + it's non-zero), and sets first to 0. */ +#define PSEP(sep, start) \ + (first ? (first = 0, (start && fputs (start, stdout))) : fputs (sep, stdout)) + + /* Actually fetch the statistics. */ + bzero (&state, sizeof (state)); /* Initialize STATE. */ + err = vm_state_refresh (&state); + if (err) + error (2, err, "vm_state_refresh"); if (terse) /* Terse (one-line) output mode. */ { int first_hdr = 1, first, repeats; - struct vm_statistics prev_stats; + struct vm_state prev_state; + int const_fields = 0; if (count == 0) count = -1; + /* We only show const fields once per page, so find out which ones + those are. */ + for (field = fields; field->name; field++) + if ((output_fields & (1 << (field - fields))) + && field->change_type == CONST) + const_fields |= (1 << (field - fields)); + output_fields &= ~const_fields; + + if (const_fields) + hdr_interval--; /* Allow room for the constant fields. */ + do { if (first_hdr) @@ -192,56 +444,74 @@ main (int argc, char **argv) else putchar ('\n'); - if (print_heading) + if (const_fields) + /* Output constant fields on a line preceding the header. */ { - int which; - for (which = 0, first = 1; which < num_fields; which++) - if (output_fields & (1 << which)) + for (field = fields, first = 1; field->name; field++) + if (const_fields & (1 << (field - fields))) { - if (first) - first = 0; + val_t val = vm_state_get_field (&state, field); + if (val < 0) + /* Couldn't fetch this field, don't try again. */ + const_fields &= ~(1 << (field - fields)); else - putchar (' '); - fputs (fields[which].hdr, stdout); + { + PSEP (", ", "("); + printf ("%s: ", field->desc); + print_val (val, 0, FDISPTYPE (field), 0, + state.vmstats.pagesize); + } + } + if (! first) + puts (")"); + } + + if (print_heading) + { + for (field = fields, first = 1; field->name; field++) + if (output_fields & (1 << (field - fields))) + { + PSEP (" ", 0); + fputs (field->hdr, stdout); } putchar ('\n'); } - bzero (&prev_stats, sizeof (prev_stats)); + prev_state = state; for (repeats = 0 ; count && repeats < hdr_interval && count ; repeats++, count--) { - /* Actually fetch the statistics. */ - err = vm_statistics (mach_task_self (), &stats); - if (err) - error (2, err, "vm_statistics"); - /* Output the fields. */ for (field = fields, first = 1; field->name; field++) if (output_fields & (1 << (field - fields))) { - integer_t val = FVAL (stats, field); + int sign = 0; int width = strlen (field->hdr); + val_t val = vm_state_get_field (&state, field); - if (field->cum) + if (repeats && field->change_type == CUMUL) { - integer_t cum = val; - val -= FVAL (prev_stats, field); - FVAL (prev_stats, field) = cum; + sign = 1; + val -= vm_state_get_field (&prev_state, field); } - if (first) - first = 0; - else - putchar (' '); - printf ("%*d", width, val); + PSEP (" ", 0); + print_val (val, width, FDISPTYPE (field), sign, + state.vmstats.pagesize); } putchar ('\n'); + prev_state = state; + if (period) - sleep (period); + { + sleep (period); + err = vm_state_refresh (&state); + if (err) + error (2, err, "vm_state_refresh"); + } } } while (count); @@ -265,19 +535,20 @@ main (int argc, char **argv) max_desc_width = desc_len; } - /* Actually fetch the statistics. */ - err = vm_statistics (mach_task_self (), &stats); - if (err) - error (2, err, "vm_statistics"); - for (field = fields; field->name; field++) if (output_fields & (1 << (field - fields))) - if (print_prefix) - printf ("%s:%*d\n", field->desc, - max_desc_width + 5 - strlen (field->desc), - FVAL (stats, field)); - else - printf ("%d\n", FVAL (stats, field)); + { + val_t val = vm_state_get_field (&state, field); + int fwidth = 0; + if (print_prefix) + { + printf ("%s:", field->desc); + fwidth = max_desc_width + 5 - strlen (field->desc); + } + print_val (val, fwidth, FDISPTYPE (field), 0, + state.vmstats.pagesize); + putchar ('\n'); + } } exit (0); |