slabtop: adapted to exploit new alloc & sort interface

In addition to the new interface several other changes
were made. In fact, there are so many I must apologize
in advance to Craig, et al. for the hatchet I wielded.

Among the changes was my attempt to shrink the body of
that main() function. It's amazing how some folks must
always cram as code much as possible into  ol' main().

Personally, unless it's a throwaway quick & dirty pgm,
all main function bodies should not exceed one screen.

Signed-off-by: Jim Warner <james.warner@comcast.net>
This commit is contained in:
Jim Warner 2015-07-11 00:00:00 -05:00 committed by Craig Small
parent 5c3542c4e1
commit e044559527

433
slabtop.c
View File

@ -21,18 +21,18 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <ncurses.h>
#include <termios.h>
#include <getopt.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <getopt.h>
#include <locale.h>
#include <ncurses.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
@ -44,36 +44,58 @@
#include "strutils.h"
#include <proc/slab.h>
#define DEFAULT_SORT_ITEM PROCPS_SLABNODE_OBJS
#define DEFAULT_SORT PROCPS_SLABNODE_OBJS
#define CHAINS_ALLOC 150
static unsigned short Cols, Rows;
static struct termios Saved_tty;
static long Delay = 3;
static int Run_once = 0;
static struct procps_slabinfo *Slab_info;
enum slabnode_item Sort_item = DEFAULT_SORT;
enum slabnode_item Node_items[] = {
PROCPS_SLABNODE_OBJS, PROCPS_SLABNODE_AOBJS, PROCPS_SLABNODE_USE,
PROCPS_SLABNODE_OBJ_SIZE, PROCPS_SLABNODE_SLABS, PROCPS_SLABNODE_OBJS_PER_SLAB,
PROCPS_SLABNODE_SIZE, PROCPS_SLABNODE_NAME,
/* last 2 are sortable but are not displayable,
thus they need not be represented in the Relative_enums */
PROCPS_SLABNODE_PAGES_PER_SLAB,
PROCPS_SLABNODE_ASLABS };
enum Relative_enums {
my_OBJS, my_AOBJS, my_USE, my_OSIZE,
my_SLABS, my_OPS, my_SIZE, my_NAME };
#define MAX_ITEMS (int)(sizeof(Node_items) / sizeof(Node_items[0]))
#define PRINT_line(fmt, ...) if (Run_once) printf(fmt, __VA_ARGS__); else printw(fmt, __VA_ARGS__)
static unsigned short cols, rows;
static struct termios saved_tty;
static long delay = 3;
static int run_once = 0;
#define print_line(fmt, ...) if (run_once) printf(fmt, __VA_ARGS__); else printw(fmt, __VA_ARGS__)
/*
* term_size - set the globals 'cols' and 'rows' to the current terminal size
* term_resize - set the globals 'Cols' and 'Rows' to the current terminal size
*/
static void term_size(int unusused __attribute__ ((__unused__)))
static void term_resize (int unusused __attribute__ ((__unused__)))
{
struct winsize ws;
if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) && ws.ws_row > 10) {
cols = ws.ws_col;
rows = ws.ws_row;
Cols = ws.ws_col;
Rows = ws.ws_row;
} else {
cols = 80;
rows = 24;
Cols = 80;
Rows = 24;
}
}
static void sigint_handler(int unused __attribute__ ((__unused__)))
static void sigint_handler (int unused __attribute__ ((__unused__)))
{
delay = 0;
Delay = 0;
}
static void __attribute__((__noreturn__)) usage(FILE *out)
static void __attribute__((__noreturn__)) usage (FILE *out)
{
fputs(USAGE_HEADER, out);
fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
@ -90,10 +112,10 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
fputs(_(" b: sort by objects per slab\n"), out);
fputs(_(" c: sort by cache size\n"), out);
fputs(_(" l: sort by number of slabs\n"), out);
fputs(_(" v: sort by number of active slabs\n"), out);
fputs(_(" v: sort by (non display) number of active slabs\n"), out);
fputs(_(" n: sort by name\n"), out);
fputs(_(" o: sort by number of objects (the default)\n"), out);
fputs(_(" p: sort by pages per slab\n"), out);
fputs(_(" p: sort by (non display) pages per slab\n"), out);
fputs(_(" s: sort by object size\n"), out);
fputs(_(" u: sort by cache utilization\n"), out);
fprintf(out, USAGE_MAN_TAIL("slabtop(1)"));
@ -103,11 +125,10 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
/*
* set_sort_func - return the slab_sort_func that matches the given key.
* On unrecognizable key, DEF_SORT_FUNC is returned.
* On unrecognizable key, DEFAULT_SORT is returned.
*/
static enum procps_slabinfo_nodeitem get_sort_item(
const char key,
enum procps_slabinfo_nodeitem old_sort)
static enum slabnode_item set_sort_item (
const char key)
{
switch (tolower(key)) {
case 'n':
@ -117,7 +138,7 @@ static enum procps_slabinfo_nodeitem get_sort_item(
case 'a':
return PROCPS_SLABNODE_AOBJS;
case 's':
return PROCPS_SLABNODE_SIZE;
return PROCPS_SLABNODE_OBJ_SIZE;
case 'b':
return PROCPS_SLABNODE_OBJS_PER_SLAB;
case 'p':
@ -131,111 +152,136 @@ static enum procps_slabinfo_nodeitem get_sort_item(
case 'u':
return PROCPS_SLABNODE_USE;
default:
return old_sort;
return DEFAULT_SORT;
}
}
#if 0
case 'Q':
delay = 0;
break;
}
}
#endif
static void print_stats(struct procps_slabinfo *info)
static void parse_opts (int argc, char **argv)
{
#define STAT_VAL(e) stats[e].result
enum stat_enums {
static const struct option longopts[] = {
{ "delay", required_argument, NULL, 'd' },
{ "sort", required_argument, NULL, 's' },
{ "once", no_argument, NULL, 'o' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }};
int o;
while ((o = getopt_long(argc, argv, "d:s:ohV", longopts, NULL)) != -1) {
switch (o) {
case 'd':
errno = 0;
Delay = strtol_or_err(optarg, _("illegal delay"));
if (Delay < 1)
xerrx(EXIT_FAILURE, _("delay must be positive integer"));
break;
case 's':
Sort_item = set_sort_item(optarg[0]);
break;
case 'o':
Run_once=1;
Delay = 0;
break;
case 'V':
printf(PROCPS_NG_VERSION);
exit(EXIT_SUCCESS);
case 'h':
usage(stdout);
default:
usage(stderr);
}
}
}
static void print_summary (void)
{
#define STAT_VAL(e) (int)stats[e].result
enum slabs_enums {
stat_AOBJS, stat_OBJS, stat_ASLABS, stat_SLABS,
stat_ACACHES, stat_CACHES, stat_ACTIVE, stat_TOTAL,
stat_MIN, stat_AVG, stat_MAX,
};
static struct procps_slabinfo_result stats[] = {
{ PROCPS_SLABINFO_AOBJS, 0, &stats[1] },
{ PROCPS_SLABINFO_OBJS, 0, &stats[2] },
{ PROCPS_SLABINFO_ASLABS, 0, &stats[3] },
{ PROCPS_SLABINFO_SLABS, 0, &stats[4] },
{ PROCPS_SLABINFO_ACACHES, 0, &stats[5] },
{ PROCPS_SLABINFO_CACHES, 0, &stats[6] },
{ PROCPS_SLABINFO_SIZE_ACTIVE, 0, &stats[7] },
{ PROCPS_SLABINFO_SIZE_TOTAL, 0, &stats[8] },
{ PROCPS_SLABINFO_SIZE_MIN, 0, &stats[9] },
{ PROCPS_SLABINFO_SIZE_AVG, 0, &stats[10] },
{ PROCPS_SLABINFO_SIZE_MAX, 0, NULL },
static struct slabs_result stats[] = {
{ PROCPS_SLABS_AOBJS, 0, &stats[1] },
{ PROCPS_SLABS_OBJS, 0, &stats[2] },
{ PROCPS_SLABS_ASLABS, 0, &stats[3] },
{ PROCPS_SLABS_SLABS, 0, &stats[4] },
{ PROCPS_SLABS_ACACHES, 0, &stats[5] },
{ PROCPS_SLABS_CACHES, 0, &stats[6] },
{ PROCPS_SLABS_SIZE_ACTIVE, 0, &stats[7] },
{ PROCPS_SLABS_SIZE_TOTAL, 0, &stats[8] },
{ PROCPS_SLABS_SIZE_MIN, 0, &stats[9] },
{ PROCPS_SLABS_SIZE_AVG, 0, &stats[10] },
{ PROCPS_SLABS_SIZE_MAX, 0, NULL },
};
if (procps_slabinfo_stat_getchain(info, stats) < 0)
xerrx(EXIT_FAILURE,
_("Error getting slabinfo results"));
if (procps_slabs_getchain(Slab_info, stats) < 0) \
xerrx(EXIT_FAILURE, _("Error getting slab summary results"));
print_line(" %-35s: %d / %d (%.1f%%)\n"
" %-35s: %d / %d (%.1f%%)\n"
" %-35s: %d / %d (%.1f%%)\n"
" %-35s: %.2fK / %.2fK (%.1f%%)\n"
" %-35s: %.2fK / %.2fK / %.2fK\n\n",
/* Translation Hint: Next five strings must not
* exceed 35 length in characters. */
/* xgettext:no-c-format */
_("Active / Total Objects (% used)"),
STAT_VAL(stat_AOBJS), STAT_VAL(stat_OBJS),
100.0 * STAT_VAL(stat_AOBJS) / STAT_VAL(stat_OBJS),
/* xgettext:no-c-format */
_("Active / Total Slabs (% used)"),
STAT_VAL(stat_ASLABS), STAT_VAL(stat_SLABS),
100.0 * STAT_VAL(stat_ASLABS) / STAT_VAL(stat_SLABS),
/* xgettext:no-c-format */
_("Active / Total Caches (% used)"),
STAT_VAL(stat_ACACHES), STAT_VAL(stat_CACHES),
100.0 * STAT_VAL(stat_ACACHES) / STAT_VAL(stat_CACHES),
/* xgettext:no-c-format */
_("Active / Total Size (% used)"),
STAT_VAL(stat_ACTIVE) / 1024.0 , STAT_VAL(stat_TOTAL) / 1024.0,
100.0 * STAT_VAL(stat_ACTIVE) / STAT_VAL(stat_TOTAL),
_("Minimum / Average / Maximum Object"),
STAT_VAL(stat_MIN) / 1024.0, STAT_VAL(stat_AVG) / 1024.0,
STAT_VAL(stat_MAX) / 1024.0);
#undef STAT_VAL
PRINT_line(" %-35s: %d / %d (%.1f%%)\n"
, /* Translation Hint: Next five strings must not
* exceed a length of 35 characters. */
/* xgettext:no-c-format */
_("Active / Total Objects (% used)")
, STAT_VAL(stat_AOBJS)
, STAT_VAL(stat_OBJS)
, 100.0 * STAT_VAL(stat_AOBJS) / STAT_VAL(stat_OBJS));
PRINT_line(" %-35s: %d / %d (%.1f%%)\n"
, /* xgettext:no-c-format */
_("Active / Total Slabs (% used)")
, STAT_VAL(stat_ASLABS)
, STAT_VAL(stat_SLABS)
, 100.0 * STAT_VAL(stat_ASLABS) / STAT_VAL(stat_SLABS));
PRINT_line(" %-35s: %d / %d (%.1f%%)\n"
, /* xgettext:no-c-format */
_("Active / Total Caches (% used)")
, STAT_VAL(stat_ACACHES)
, STAT_VAL(stat_CACHES)
, 100.0 * STAT_VAL(stat_ACACHES) / STAT_VAL(stat_CACHES));
PRINT_line(" %-35s: %.2fK / %.2fK (%.1f%%)\n"
, /* xgettext:no-c-format */
_("Active / Total Size (% used)")
, STAT_VAL(stat_ACTIVE) / 1024.0
, STAT_VAL(stat_TOTAL) / 1024.0
, 100.0 * STAT_VAL(stat_ACTIVE) / STAT_VAL(stat_TOTAL));
PRINT_line(" %-35s: %.2fK / %.2fK / %.2fK\n\n"
, _("Minimum / Average / Maximum Object")
, STAT_VAL(stat_MIN) / 1024.0
, STAT_VAL(stat_AVG) / 1024.0
, STAT_VAL(stat_MAX) / 1024.0);
#undef STAT_VAL
}
static void cleanup(const int is_tty, struct procps_slabinfo **slab_info)
static void print_headings (void)
{
if (is_tty)
tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty);
if (!run_once)
endwin();
procps_slabinfo_unref(slab_info);
/* Translation Hint: Please keep alignment of the
* following intact. */
PRINT_line("%-78s\n", _(" OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME"));
}
static void print_details (struct slabnode_chain *chain)
{
#define my_NUM(c,e) (unsigned)c->head[e].result.num
#define my_STR(c,e) c->head[e].result.str
PRINT_line("%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n"
, my_NUM(chain, my_OBJS), my_NUM(chain, my_AOBJS)
, my_NUM(chain, my_USE), my_NUM(chain, my_OSIZE) / 1024.0
, my_NUM(chain, my_SLABS), my_NUM(chain, my_OPS)
, my_NUM(chain, my_SIZE) / 1024
, my_STR(chain, my_NAME));
return;
#undef my_NUM
#undef my_STR
}
int main(int argc, char *argv[])
{
int is_tty, o;
int nr_slabs;
int is_tty, nr_slabs, rc = EXIT_SUCCESS;
unsigned short old_rows;
int retval = EXIT_SUCCESS;
struct procps_slabinfo *slab_info;
enum procps_slabinfo_nodeitem sort_item = DEFAULT_SORT_ITEM;
static const struct option longopts[] = {
{ "delay", required_argument, NULL, 'd' },
{ "sort", required_argument, NULL, 's' },
{ "once", no_argument, NULL, 'o' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
struct procps_slabnode_result result[] = {
{ PROCPS_SLABNODE_OBJS, 0, &result[1] },
{ PROCPS_SLABNODE_AOBJS, 0, &result[2] },
{ PROCPS_SLABNODE_USE, 0, &result[3] },
{ PROCPS_SLABNODE_OBJ_SIZE, 0, &result[4] },
{ PROCPS_SLABNODE_SLABS, 0, &result[5] },
{ PROCPS_SLABNODE_OBJS_PER_SLAB, 0, &result[6] },
{ PROCPS_SLABNODE_SIZE, 0, NULL } };
enum result_enums {
stat_OBJS, stat_AOBJS, stat_USE, stat_OSIZE, stat_SLABS,
stat_OPS, stat_SIZE };
struct slabnode_chain **v;
#ifdef HAVE_PROGRAM_INVOCATION_NAME
program_invocation_name = program_invocation_short_name;
@ -245,121 +291,84 @@ int main(int argc, char *argv[])
textdomain(PACKAGE);
atexit(close_stdout);
while ((o = getopt_long(argc, argv, "d:s:ohV", longopts, NULL)) != -1) {
switch (o) {
case 'd':
errno = 0;
delay = strtol_or_err(optarg, _("illegal delay"));
if (delay < 1)
xerrx(EXIT_FAILURE,
_("delay must be positive integer"));
break;
case 's':
sort_item = get_sort_item(optarg[0], sort_item);
break;
case 'o':
run_once=1;
delay = 0;
break;
case 'V':
printf(PROCPS_NG_VERSION);
return EXIT_SUCCESS;
case 'h':
usage(stdout);
default:
usage(stderr);
}
}
parse_opts(argc, argv);
if (procps_slabinfo_new(&slab_info) < 0)
xerrx(EXIT_FAILURE,
_("Unable to create slabinfo structure"));
if (procps_slabinfo_new(&Slab_info) < 0)
xerrx(EXIT_FAILURE, _("Unable to create slabinfo structure"));
if (!(v = procps_slabnode_chains_alloc(Slab_info, CHAINS_ALLOC, 0, MAX_ITEMS, Node_items)))
xerrx(EXIT_FAILURE, _("Unable to allocate slabinfo nodes"));
is_tty = isatty(STDIN_FILENO);
if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1)
xwarn(_("terminal setting retrieval"));
old_rows = rows;
term_size(0);
if (!run_once) {
if (!Run_once) {
is_tty = isatty(STDIN_FILENO);
if (is_tty && tcgetattr(STDIN_FILENO, &Saved_tty) == -1)
xwarn(_("terminal setting retrieval"));
old_rows = Rows;
term_resize(0);
initscr();
resizeterm(rows, cols);
signal(SIGWINCH, term_size);
resizeterm(Rows, Cols);
signal(SIGWINCH, term_resize);
signal(SIGINT, sigint_handler);
}
signal(SIGINT, sigint_handler);
#define STAT_VAL(e) result[e].result
do {
char *slab_name;
struct timeval tv;
fd_set readfds;
char c;
int i, myerrno;
int i;
if (procps_slabinfo_read(slab_info) < 0) {
xwarn(_("Unable to read slabinfo"));
retval = EXIT_FAILURE;
// this next guy also performs the procps_slabnode_read() call
if ((nr_slabs = procps_slabnode_chains_fill(Slab_info, v, CHAINS_ALLOC)) < 0) {
xwarn(_("Unable to get slabinfo node data"));
rc = EXIT_FAILURE;
break;
}
if (!(v = procps_slabnode_chains_sort(Slab_info, v, nr_slabs, Sort_item))) {
xwarn(_("Unable to sort slab nodes"));
rc = EXIT_FAILURE;
break;
}
if (!run_once && old_rows != rows) {
resizeterm(rows, cols);
old_rows = rows;
if (Run_once) {
print_summary();
print_headings();
for (i = 0; i < nr_slabs; i++)
print_details(v[i]);
break;
}
if (old_rows != Rows) {
resizeterm(Rows, Cols);
old_rows = Rows;
}
move(0, 0);
print_stats(slab_info);
if (procps_slabinfo_sort(slab_info, sort_item) < 0) {
xwarn(_("Unable to sort slabnodes"));
retval= EXIT_FAILURE;
break;
}
print_summary();
attron(A_REVERSE);
/* Translation Hint: Please keep alignment of the
* following intact. */
print_line("%-78s\n", _(" OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME"));
print_headings();
attroff(A_REVERSE);
if ((nr_slabs = procps_slabinfo_node_count(slab_info)) < 0) {
xwarn(_("Unable to count slabinfo nodes"));
retval = EXIT_FAILURE;
break;
}
for (i = 0; i < Rows - 8 && i < nr_slabs; i++)
print_details(v[i]);
for (i=0 ; i < rows - 8 && i < nr_slabs; i++) {
if (procps_slabinfo_node_getchain(slab_info, result, i) < 0) {
xwarn(_("Unable to get slabinfo node data"));
retval = EXIT_FAILURE;
refresh();
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
tv.tv_sec = Delay;
tv.tv_usec = 0;
if (select(STDOUT_FILENO, &readfds, NULL, NULL, &tv) > 0) {
char c;
if (read(STDIN_FILENO, &c, 1) != 1
|| (c == 'Q' || c == 'q'))
break;
}
slab_name= procps_slabinfo_node_getname(slab_info, i);
print_line("%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n",
STAT_VAL(stat_OBJS), STAT_VAL(stat_AOBJS),
STAT_VAL(stat_USE),
STAT_VAL(stat_OSIZE) / 1024.0, STAT_VAL(stat_SLABS),
STAT_VAL(stat_OPS),
(unsigned)(STAT_VAL(stat_SIZE) / 1024),
slab_name?slab_name:"(unknown)");
Sort_item = set_sort_item(c);
}
if (!run_once) {
refresh();
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
tv.tv_sec = delay;
tv.tv_usec = 0;
if (select(STDOUT_FILENO, &readfds, NULL, NULL, &tv) > 0) {
if (read(STDIN_FILENO, &c, 1) != 1)
break;
if (c == 'Q' || c == 'q')
delay = 0;
else
sort_item = get_sort_item(c, sort_item);
}
}
} while (delay);
cleanup(is_tty, &slab_info);
// made zero by sigint_handler()
} while (Delay);
if (!Run_once) {
if (is_tty)
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Saved_tty);
endwin();
}
procps_slabinfo_unref(&Slab_info);
return rc;
}