diff options
Diffstat (limited to 'utils/vmstat.c')
-rw-r--r-- | utils/vmstat.c | 332 |
1 files changed, 218 insertions, 114 deletions
diff --git a/utils/vmstat.c b/utils/vmstat.c index c7f7a7af..7d852992 100644 --- a/utils/vmstat.c +++ b/utils/vmstat.c @@ -1,8 +1,7 @@ /* Print vm statistics - Copyright (C) 1996 Free Software Foundation, Inc. - - Written by Miles Bader <miles@gnu.ai.mit.edu> + Copyright (C) 1996,97,99,2002 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -25,30 +24,30 @@ #include <string.h> #include <unistd.h> #include <stdlib.h> +#include <fcntl.h> +#include <version.h> #include <mach.h> #include <mach/vm_statistics.h> #include <mach/default_pager.h> #include <hurd.h> +#include <hurd/paths.h> -char *argp_program_version = "vmstat 1.1 (GNU " HURD_RELEASE ")"; +const char *argp_program_version = STANDARD_HURD_VERSION (vmstat); static const struct argp_option options[] = { - {"terse", 't', 0, 0, "Use short one-line output format", 1 }, + {"terse", 't', 0, 0, "Use short one-line output format"}, {"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" +static const char *doc = "Show system virtual memory statistics" +"\vIf 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" @@ -61,6 +60,7 @@ static const char *doc = "If PERIOD is supplied, then terse mode is" than what the system returns values in, as we represent some quantities as bytes instead of pages)! */ typedef long long val_t; +#define BADVAL ((val_t) -1LL) /* a good generic value for "couldn't get" */ /* What a given number describes. */ enum val_type @@ -71,6 +71,31 @@ enum val_type PCENT, /* Append `%'. */ }; +/* Return the `nominal' width of a field of type TYPE, in units of SIZE_UNITS. */ +static size_t +val_width (val_t val, enum val_type type, size_t size_units) +{ + size_t vwidth (val_t val) + { + size_t w = 1; + if (val < 0) + w++, val = -val; + while (val > 9) + w++, val /= 10; + return w; + } + if (type == PCENT) + return vwidth (val) + 1; + else if ((type == SIZE || type == PAGESZ) && size_units == 0) + return val > 1000 ? 5 : vwidth (val) + 1; + else + { + if ((type == SIZE || type == PAGESZ) && size_units > 0) + val /= size_units; + return vwidth (val); + } +} + /* Print a number of type TYPE. If SIZE_UNITS is non-zero, then values of type SIZE are divided by that amount and printed without a suffix. FWIDTH is the width of the field to print it in, right-justified. If SIGN is @@ -80,13 +105,13 @@ print_val (val_t val, enum val_type type, size_t size_units, int fwidth, int sign) { if (type == PCENT) - printf (sign ? "%+*d%%" : "%*d%%", fwidth - 1, val); + printf (sign ? "%+*lld%%" : "%*lld%%", fwidth - 1, val); else if ((type == SIZE || type == PAGESZ) && size_units == 0) { float fval = val; char *units = " KMGT", *u = units; - while (fval > 1024) + while (fval >= 10000) { fval /= 1024; u++; @@ -101,10 +126,14 @@ print_val (val_t val, enum val_type type, { if ((type == SIZE || type == PAGESZ) && size_units > 0) val /= size_units; - printf (sign ? "%+*d" : "%*d", fwidth, val); + printf (sign ? "%+*lld" : "%*lld", fwidth, val); } } +/* Special values for val_t ranges. */ +#define VAL_MAX_MEM -1 /* up to the system memory size */ +#define VAL_MAX_SWAP -2 /* up to the system swap size */ + /* How this field changes with time. */ enum field_change_type { @@ -117,15 +146,15 @@ struct vm_state; /* fwd */ struct field { - /* Name of the field; used for the option name. */ + /* Name of the field. */ char *name; - /* A descriptive title used for long output format. */ - char *desc; - /* Terse header used for the columnar style output. */ char *hdr; + /* A description of this field (for user help). */ + char *doc; + /* Type of this field. */ enum field_change_type change_type; @@ -134,6 +163,9 @@ struct field user. */ enum val_type type; + /* The `maximum value' this field can have -- used for field widths. */ + val_t max; + /* True if we display this field by default (user can always override). */ int standard :1; @@ -148,7 +180,7 @@ struct field }; /* State about system vm from which we compute the above defined fields. */ -struct vm_state +struct vm_state { /* General vm statistics. */ struct vm_statistics vmstats; @@ -167,7 +199,7 @@ vm_state_refresh (struct vm_state *state) return err; /* Mark the info as invalid, but leave DEF_PAGER alone. */ - bzero (&state->def_pager_info, sizeof state->def_pager_info); + memset (&state->def_pager_info, 0, sizeof state->def_pager_info); return 0; } @@ -198,7 +230,7 @@ vm_state_get_field (struct vm_state *state, const struct field *field) } static val_t -get_cache_hit_ratio (struct vm_state *state, const struct field *field) +get_memobj_hit_ratio (struct vm_state *state, const struct field *field) { return state->vmstats.hits * 100 / state->vmstats.lookups; } @@ -215,89 +247,125 @@ ensure_def_pager_info (struct vm_state *state) mach_port_t host; err = get_privileged_ports (&host, 0); - if (err) + if (err == EPERM) { - error (0, err, "get_privileged_ports"); - return 0; + /* We are not root, so try opening the /servers file. */ + state->def_pager = file_name_lookup (_SERVERS_DEFPAGER, O_READ, 0); + if (state->def_pager == MACH_PORT_NULL) + { + error (0, errno, _SERVERS_DEFPAGER); + return 0; + } } + if (state->def_pager == MACH_PORT_NULL) + { + 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); + 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; + } + } + } - if (err) + if (!MACH_PORT_VALID (state->def_pager)) + { + if (state->def_pager == MACH_PORT_NULL) { - error (0, err, "vm_set_default_memory_manager"); - return 0; + error (0, 0, + "No default pager running, so no swap information available"); + state->def_pager = MACH_PORT_DEAD; /* so we don't try again */ } + 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; -} +#define SWAP_FIELD(getter, expr) \ + static val_t getter (struct vm_state *state, const struct field *field) \ + { return ensure_def_pager_info (state) ? (val_t) (expr) : BADVAL; } -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; -} +SWAP_FIELD (get_swap_size, state->def_pager_info.dpi_total_space) +SWAP_FIELD (get_swap_free, state->def_pager_info.dpi_free_space) +SWAP_FIELD (get_swap_page_size, state->def_pager_info.dpi_page_size) +SWAP_FIELD (get_swap_active, (state->def_pager_info.dpi_total_space + - state->def_pager_info.dpi_free_space)) /* Returns the byte offset of the field FIELD in a vm_statistics structure. */ #define _F(field_name) offsetof (struct vm_statistics, field_name) +#define K 1024 +#define M (1024*K) +#define G (1024LL*M) + /* vm_statistics fields we know about. */ static const struct field fields[] = { - {"pagesize", "Pagesize", " pgsz", CONST,PAGESZ, 1,_F(pagesize)}, - {"size", "Size", " size", CONST,SIZE, 1,0,get_size}, - {"free", "Free", " free", VARY, SIZE, 1,_F(free_count)}, - {"active", "Active", " actv", VARY, SIZE, 1,_F(active_count)}, - {"inactive", "Inactive", "inact", VARY, SIZE, 1,_F(inactive_count)}, - {"wired", "Wired", "wired", VARY, SIZE, 1,_F(wire_count)}, - {"zero-filled", "Zeroed", "zeroed", CUMUL,SIZE, 1,_F(zero_fill_count)}, - {"reactivated", "Reactivated", "react", CUMUL,SIZE, 1,_F(reactivations)}, - {"pageins", "Pageins", "pgins", CUMUL,SIZE, 1,_F(pageins)}, - {"pageouts", "Pageouts", "pgouts", CUMUL,SIZE, 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,SIZE, 1,0,get_swap_size}, - {"swap-active", "Swap active", "swactv", VARY, SIZE, 0,0,get_swap_active}, - {"swap-free", "Swap free", "swfree", VARY, SIZE, 1,0,get_swap_free}, - {"swap-pagesize","Swap pagesize","swpgsz", CONST,PAGESZ, 0,0,get_swap_page_size}, + {"pagesize", "pgsz", "System pagesize", + CONST, PAGESZ, 16*K, 1, _F (pagesize) }, + {"size", "size", "Usable physical memory", + CONST, SIZE, VAL_MAX_MEM, 1, 0, get_size }, + {"free", "free", "Unused physical memory", + VARY, SIZE, VAL_MAX_MEM, 1, _F (free_count) }, + {"active", "actv", "Physical memory in active use", + VARY, SIZE, VAL_MAX_MEM, 1, _F (active_count) }, + {"inactive", "inact", "Physical memory in the inactive queue", + VARY, SIZE, VAL_MAX_MEM, 1, _F (inactive_count) }, + {"wired", "wired", "Unpageable physical memory", + VARY, SIZE, VAL_MAX_MEM, 1, _F (wire_count) }, + {"zero filled", "zeroed","Cumulative zero-filled pages", + CUMUL, SIZE, 90*G, 1, _F (zero_fill_count) }, + {"reactivated", "react", "Cumulative reactivated inactive pages", + CUMUL, SIZE, 900*M, 1, _F (reactivations) }, + {"pageins", "pgins", "Cumulative pages paged in", + CUMUL, SIZE, 90*G, 1, _F (pageins) }, + {"pageouts", "pgouts","Cumulative pages paged out", + CUMUL, SIZE, 90*G, 1, _F (pageouts) }, + {"page faults", "pfaults","Cumulative page faults", + CUMUL, COUNT, 99999999, 1, _F (faults) }, + {"cow faults", "cowpfs", "Cumulative copy-on-write page faults", + CUMUL, COUNT, 9999999, 1, _F (cow_faults) }, + {"memobj lookups","lkups","Memory-object lookups", + CUMUL, COUNT, 999999, 0, _F (lookups) }, + {"memobj hits", "hits", "Memory-object lookups with active pagers", + CUMUL, COUNT, 999999, 0, _F (hits) }, + {"memobj hit ratio","hrat","Percentage of memory-object lookups with active pagers", + VARY, PCENT, 99, 1, -1, get_memobj_hit_ratio }, + {"swap size", "swsize", "Size of the default-pager swap area", + CONST, SIZE, VAL_MAX_SWAP, 1, 0 ,get_swap_size }, + {"swap active", "swactv", "Default-pager swap area in use", + VARY, SIZE, VAL_MAX_SWAP, 0, 0 ,get_swap_active }, + {"swap free", "swfree", "Default-pager swap area available for swapping", + VARY, SIZE, VAL_MAX_SWAP, 1, 0 ,get_swap_free }, + {"swap pagesize","swpgsz", "Units used for swapping to the default pager", + CONST, PAGESZ, 16*K, 0, 0 ,get_swap_page_size }, {0} }; #undef _F +/* Convert a field name to the corresponding user-option. */ +static char *name_to_option (const char *name) +{ + char *opt = strdup (name), *p; + if (opt) + for (p = opt; *p; p++) + if (*p == ' ') + *p = '-'; + return opt; +} + int main (int argc, char **argv) { @@ -327,7 +395,7 @@ main (int argc, char **argv) case 'H': print_heading = 0; break; case 'b': size_units = 1; break; case 'v': size_units = -1; break; - case 'k': size_units = 1024; break; + case 'k': size_units = K; break; case ARGP_KEY_ARG: terse = 1; @@ -352,8 +420,9 @@ main (int argc, char **argv) struct argp_option *field_opts; int field_opts_size; struct argp field_argp = { 0, parse_opt }; - const struct argp *parents[] = { &field_argp, 0 }; - const struct argp argp = { options, parse_opt, args_doc, doc, parents }; + const struct argp_child children[] = + {{&field_argp, 0, "Selecting which statistics to show:"}, {0}}; + const struct argp argp = { options, parse_opt, args_doc, doc, children }; /* See how many fields we know about. */ for (field = fields; field->name; field++) @@ -362,19 +431,19 @@ main (int argc, char **argv) /* Construct an options vector for them. */ field_opts_size = ((num_fields + 1) * sizeof (struct argp_option)); field_opts = alloca (field_opts_size); - bzero (field_opts, field_opts_size); + memset (field_opts, 0, field_opts_size); for (field = fields; field->name; field++) { int which = field - fields; struct argp_option *opt = &field_opts[which]; - opt->name = field->name; + opt->name = name_to_option (field->name); opt->key = -1 - which; /* options are numbered -1 ... -(N - 1). */ - opt->doc = field->desc; + opt->doc = field->doc; opt->group = 2; } - /* No need to terminate FIELD_OPTS because the bzero above's done so. */ + /* No need to terminate FIELD_OPTS because the memset above has done so. */ field_argp.options = field_opts; @@ -387,10 +456,10 @@ main (int argc, char **argv) if (field->standard) output_fields |= (1 << (field - fields)); - /* Returns an appropiate SIZE_UNITS for printing FIELD. */ -#define SIZE_UNITS(field) \ - (size_units >= 0 \ - ? size_units \ + /* Returns an appropriate SIZE_UNITS for printing FIELD. */ +#define SIZE_UNITS(field) \ + (size_units >= 0 \ + ? size_units \ : ((field)->type == PAGESZ ? 0 : state.vmstats.pagesize)) /* Prints SEP if the variable FIRST is 0, otherwise, prints START (if @@ -399,9 +468,15 @@ main (int argc, char **argv) (first ? (first = 0, (start && fputs (start, stdout))) : fputs (sep, stdout)) #define PVAL(val, field, width, sign) \ print_val (val, (field)->type, SIZE_UNITS (field), width, sign) - + /* Intuit the likely maximum field width of FIELD. */ +#define FWIDTH(field) \ + val_width ((field)->max == VAL_MAX_MEM ? get_size (&state, field) \ + : (field)->max == VAL_MAX_SWAP ? get_swap_size (&state, field) \ + : (field)->max, \ + (field)->type, SIZE_UNITS (field)) + /* Actually fetch the statistics. */ - bzero (&state, sizeof (state)); /* Initialize STATE. */ + memset (&state, 0, sizeof (state)); /* Initialize STATE. */ err = vm_state_refresh (&state); if (err) error (2, err, "vm_state_refresh"); @@ -429,6 +504,9 @@ main (int argc, char **argv) do { + int num; + int fwidths[num_fields]; + if (first_hdr) first_hdr = 0; else @@ -447,7 +525,7 @@ main (int argc, char **argv) else { PSEP (", ", "("); - printf ("%s: ", field->desc); + printf ("%s: ", field->name); PVAL (val, field, 0, 0); } } @@ -455,39 +533,62 @@ main (int argc, char **argv) puts (")"); } + /* Calculate field widths. */ + for (field = fields, num = 0; field->name; field++, num++) + if (output_fields & (1 << (field - fields))) + { + fwidths[num] = FWIDTH (field); + if (count != 1 && size_units == 0 + && field->change_type == CUMUL && field->type == SIZE) + /* We may be printing a `+' prefix for field changes, and + since this is using the mostly constant-width SIZE + notation, individual changes may be the same width as + appropriated for absolute values -- so reserver another + column for the `+' character. */ + fwidths[num]++; + if (fwidths[num] < strlen (field->hdr)) + fwidths[num] = strlen (field->hdr); + } + if (print_heading) { - for (field = fields, first = 1; field->name; field++) + for (field = fields, num = 0, first = 1; field->name; field++, num++) if (output_fields & (1 << (field - fields))) { PSEP (" ", 0); - fputs (field->hdr, stdout); + fprintf (stdout, "%*s", fwidths[num], field->hdr); } putchar ('\n'); } - + prev_state = state; for (repeats = 0 - ; count && repeats < hdr_interval && count + ; count && repeats < hdr_interval ; repeats++, count--) { /* Output the fields. */ - for (field = fields, first = 1; field->name; field++) + for (field = fields, num = 0, first = 1; field->name; field++, num++) if (output_fields & (1 << (field - fields))) { - int sign = 0; - int width = strlen (field->hdr); val_t val = vm_state_get_field (&state, field); - if (repeats && field->change_type == CUMUL) + if (val < 0) + /* Couldn't fetch this field, don't try again. */ + const_fields &= ~(1 << (field - fields)); + else { - sign = 1; - val -= vm_state_get_field (&prev_state, field); - } + int sign = 0; - PSEP (" ", 0); - PVAL (val, field, width, sign); + if (repeats && field->change_type == CUMUL) + { + sign = 1; + val -= vm_state_get_field (&prev_state, field); + } + + PSEP (" ", 0); + PVAL (val, field, fwidths[num], sign); + } } putchar ('\n'); @@ -507,7 +608,7 @@ main (int argc, char **argv) else /* Verbose output. */ { - int max_desc_width = 0; + int max_width = 0; if (print_prefix < 0) /* By default, only print a prefix if there are multiple fields. */ @@ -518,23 +619,26 @@ main (int argc, char **argv) for (field = fields; field->name; field++) if (output_fields & (1 << (field - fields))) { - int desc_len = strlen (field->desc); - if (desc_len > max_desc_width) - max_desc_width = desc_len; + int width = strlen (field->name) + FWIDTH (field); + if (width > max_width) + max_width = width; } for (field = fields; field->name; field++) if (output_fields & (1 << (field - fields))) { val_t val = vm_state_get_field (&state, field); - int fwidth = 0; - if (print_prefix) + if (val >= 0) { - printf ("%s:", field->desc); - fwidth = max_desc_width + 5 - strlen (field->desc); + int fwidth = 0; + if (print_prefix) + { + printf ("%s: ", field->name); + fwidth = max_width - strlen (field->name); + } + PVAL (val, field, fwidth, 0); + putchar ('\n'); } - PVAL (val, field, fwidth, 0); - putchar ('\n'); } } |