diff options
Diffstat (limited to 'src/slabtop.c')
-rw-r--r-- | src/slabtop.c | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/src/slabtop.c b/src/slabtop.c new file mode 100644 index 0000000..40ce340 --- /dev/null +++ b/src/slabtop.c @@ -0,0 +1,388 @@ +/* + * slabtop.c - utility to display kernel slab information. + * + * Copyright © 2009-2023 Craig Small <csmall@dropbear.xyz> + * Copyright © 2011-2023 Jim Warner <james.warner@comcast.net> + * Copyright © 2011-2012 Sami Kerola <kerolasa@iki.fi> + * Copyright © 2002-2004 Albert Cahalan + * Copyright © 2003 Chris Rivera <cmrivera@ufl.edu> + * Copyright © 2003 Robert Love <rml@tech9.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <ctype.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> +#include <unistd.h> + +#include "c.h" +#include "fileutils.h" +#include "nls.h" +#include "strutils.h" + +#include "slabinfo.h" + +#define DEFAULT_SORT SLAB_NUM_OBJS +#define CHAINS_ALLOC 150 +#define MAXTBL(t) (int)( sizeof(t) / sizeof(t[0]) ) +#define DEFAULT_DELAY 3 + +static unsigned short Cols, Rows; +static struct termios Saved_tty; +static long Delay = 0; +static int Run_once = 0; + +static struct slabinfo_info *Slab_info; + +enum slabinfo_item Sort_item = DEFAULT_SORT; +enum slabinfo_sort_order Sort_Order = SLABINFO_SORT_DESCEND; + +enum slabinfo_item Node_items[] = { + SLAB_NUM_OBJS, SLAB_ACTIVE_OBJS, SLAB_PERCENT_USED, + SLAB_OBJ_SIZE, SLAB_NUMS_SLABS, SLAB_OBJ_PER_SLAB, + SLAB_SIZE_TOTAL, SLAB_NAME, + /* next 2 are sortable but are not displayable, + thus they need not be represented in the Relative_enums */ + SLAB_PAGES_PER_SLAB, SLAB_ACTIVE_SLABS }; + +enum Relative_node { + nod_OBJS, nod_AOBJS, nod_USE, nod_OSIZE, + nod_SLABS, nod_OPS, nod_SIZE, nod_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__) + + +/* + * term_resize - set the globals 'Cols' and 'Rows' to the current terminal size + */ +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; + } else { + Cols = 80; + Rows = 24; + } +} + +static void sigint_handler (int unused __attribute__ ((__unused__))) +{ + Delay = 0; +} + +static void __attribute__((__noreturn__)) usage (FILE *out) +{ + fputs(USAGE_HEADER, out); + fprintf(out, _(" %s [options]\n"), program_invocation_short_name); + fputs(USAGE_OPTIONS, out); + fputs(_(" -d, --delay <secs> delay updates\n"), out); + fputs(_(" -o, --once only display once, then exit\n"), out); + fputs(_(" -s, --sort <char> specify sort criteria by character (see below)\n"), out); + fputs(USAGE_SEPARATOR, out); + fputs(USAGE_HELP, out); + fputs(USAGE_VERSION, out); + + fputs(_("\nThe following are valid sort criteria:\n"), out); + fputs(_(" a: sort by number of active objects\n"), 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 (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 (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)")); + + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); +} + +static void set_sort_stuff (const char key) +{ + Sort_item = DEFAULT_SORT; + Sort_Order = SLABINFO_SORT_DESCEND; + + switch (tolower(key)) { + case 'n': + Sort_item = SLAB_NAME; + Sort_Order = SLABINFO_SORT_ASCEND; + break; + case 'o': + Sort_item = SLAB_NUM_OBJS; + break; + case 'a': + Sort_item = SLAB_ACTIVE_OBJS; + break; + case 's': + Sort_item = SLAB_OBJ_SIZE; + break; + case 'b': + Sort_item = SLAB_OBJ_PER_SLAB; + break; + case 'p': + Sort_item = SLAB_PAGES_PER_SLAB; + break; + case 'l': + Sort_item = SLAB_NUMS_SLABS; + break; + case 'v': + Sort_item = SLAB_ACTIVE_SLABS; + break; + case 'c': + Sort_item = SLAB_SIZE_TOTAL; + break; + case 'u': + Sort_item = SLAB_PERCENT_USED; + break; + default: + break; + } +} + +static void parse_opts (int argc, char **argv) +{ + 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': + if (Run_once) + xerrx(EXIT_FAILURE, _("Cannot combine -d and -o options")); + errno = 0; + Delay = strtol_or_err(optarg, _("illegal delay")); + if (Delay < 1) + xerrx(EXIT_FAILURE, _("delay must be positive integer")); + break; + case 's': + set_sort_stuff(optarg[0]); + break; + case 'o': + if (Delay != 0) + xerrx(EXIT_FAILURE, _("Cannot combine -d and -o options")); + Run_once=1; + break; + case 'V': + printf(PROCPS_NG_VERSION); + exit(EXIT_SUCCESS); + case 'h': + usage(stdout); + default: + usage(stderr); + } + } + if (optind != argc) + usage(stderr); + if (!Run_once && Delay == 0) + Delay = DEFAULT_DELAY; +} + +static void print_summary (void) +{ + #define totalVAL(e,t) SLABINFO_VAL(e, t, p, Slab_info) + enum slabinfo_item items[] = { + SLABS_ACTIVE_OBJS, SLABS_NUM_OBJS, + SLABS_ACTIVE_SLABS, SLABS_NUMS_SLABS, + SLABS_CACHES_ACTIVE, SLABS_CACHES_TOTAL, + SLABS_SIZE_ACTIVE, SLABS_SIZE_TOTAL, + SLABS_OBJ_SIZE_MIN, SLABS_OBJ_SIZE_AVG, + SLABS_OBJ_SIZE_MAX + }; + enum rel_items { + tot_AOBJS, tot_OBJS, tot_ASLABS, tot_SLABS, + tot_ACACHES, tot_CACHES, tot_ACTIVE, tot_TOTAL, + tot_MIN, tot_AVG, tot_MAX + }; + struct slabinfo_stack *p; + + if (!(p = procps_slabinfo_select(Slab_info, items, MAXTBL(items)))) + xerrx(EXIT_FAILURE, _("Error getting slab summary results")); + + PRINT_line(" %-35s: %u / %u (%.1f%%)\n" + , /* Translation Hint: Next five strings must not + * exceed a length of 35 characters. */ + /* xgettext:no-c-format */ + _("Active / Total Objects (% used)") + , totalVAL(tot_AOBJS, u_int) + , totalVAL(tot_OBJS, u_int) + , 100.0 * totalVAL(tot_AOBJS, u_int) / totalVAL(tot_OBJS, u_int)); + PRINT_line(" %-35s: %u / %u (%.1f%%)\n" + , /* xgettext:no-c-format */ + _("Active / Total Slabs (% used)") + , totalVAL(tot_ASLABS, u_int) + , totalVAL(tot_SLABS, u_int) + , 100.0 * totalVAL(tot_ASLABS, u_int) / totalVAL(tot_SLABS, u_int)); + PRINT_line(" %-35s: %u / %u (%.1f%%)\n" + , /* xgettext:no-c-format */ + _("Active / Total Caches (% used)") + , totalVAL(tot_ACACHES, u_int) + , totalVAL(tot_CACHES, u_int) + , 100.0 * totalVAL(tot_ACACHES, u_int) / totalVAL(tot_CACHES, u_int)); + PRINT_line(" %-35s: %.2fK / %.2fK (%.1f%%)\n" + , /* xgettext:no-c-format */ + _("Active / Total Size (% used)") + , totalVAL(tot_ACTIVE, ul_int) / 1024.0 + , totalVAL(tot_TOTAL, ul_int) / 1024.0 + , 100.0 * totalVAL(tot_ACTIVE, ul_int) / totalVAL(tot_TOTAL, ul_int)); + PRINT_line(" %-35s: %.2fK / %.2fK / %.2fK\n\n" + , _("Minimum / Average / Maximum Object") + , totalVAL(tot_MIN, u_int) / 1024.0 + , totalVAL(tot_AVG, u_int) / 1024.0 + , totalVAL(tot_MAX, u_int) / 1024.0); + #undef totalVAL +} + +static void print_headings (void) +{ + /* 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 slabinfo_stack *stack) +{ + #define nodeVAL(e,t) SLABINFO_VAL(e, t, stack, Slab_info) + PRINT_line("%6u %6u %3u%% %7.2fK %6u %8u %9luK %-23s\n" + , nodeVAL(nod_OBJS, u_int) + , nodeVAL(nod_AOBJS, u_int) + , nodeVAL(nod_USE, u_int) + , nodeVAL(nod_OSIZE, u_int) / 1024.0 + , nodeVAL(nod_SLABS, u_int) + , nodeVAL(nod_OPS, u_int) + , nodeVAL(nod_SIZE, ul_int) / 1024 + , nodeVAL(nod_NAME, str)); + + return; + #undef nodeVAL +} + + +int main(int argc, char *argv[]) +{ + int is_tty = 0, rc = EXIT_SUCCESS; + unsigned short old_rows = 0; + +#ifdef HAVE_PROGRAM_INVOCATION_NAME + program_invocation_name = program_invocation_short_name; +#endif + setlocale (LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + parse_opts(argc, argv); + + if (procps_slabinfo_new(&Slab_info) < 0) + xerr(EXIT_FAILURE, _("Unable to create slabinfo structure")); + + 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_resize); + signal(SIGINT, sigint_handler); + } + + do { + struct slabinfo_reaped *reaped; + struct timeval tv; + fd_set readfds; + int i; + + if (!(reaped = procps_slabinfo_reap(Slab_info, Node_items, MAXTBL(Node_items)))) { + xwarn(_("Unable to get slabinfo node data")); + rc = EXIT_FAILURE; + break; + } + + if (!(procps_slabinfo_sort(Slab_info, reaped->stacks, reaped->total, Sort_item, Sort_Order))) { + xwarn(_("Unable to sort slab nodes")); + rc = EXIT_FAILURE; + break; + } + + if (Run_once) { + print_summary(); + print_headings(); + for (i = 0; i < reaped->total; i++) + print_details(reaped->stacks[i]); + break; + } + + if (old_rows != Rows) { + resizeterm(Rows, Cols); + old_rows = Rows; + } + move(0, 0); + print_summary(); + attron(A_REVERSE); + print_headings(); + attroff(A_REVERSE); + + for (i = 0; i < Rows - 8 && i < reaped->total; i++) + print_details(reaped->stacks[i]); + + 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; + set_sort_stuff(c); + } + // 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; +} |