/* * slabtop.c - utility to display kernel slab information. * * Chris Rivera * Robert Love * * This program is licensed under the GNU Library General Public License, v2 * * Copyright (C) 2003 Chris Rivera */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "proc/slab.h" #include "proc/version.h" #define DEF_SORT_FUNC sort_nr_objs #define SLAB_STAT_ZERO { nr_objs: 0 } static unsigned short cols, rows; static struct termios saved_tty; static long delay = 3; static int (*sort_func)(const struct slab_info *, const struct slab_info *); static struct slab_info *merge_objs(struct slab_info *a, struct slab_info *b) { struct slab_info sorted_list; struct slab_info *curr = &sorted_list; while ((a != NULL) && (b != NULL)) { if (sort_func(a, b)) { curr->next = a; curr = a; a = a->next; } else { curr->next = b; curr = b; b = b->next; } } curr->next = (a == NULL) ? b : a; return sorted_list.next; } /* * slabsort - merge sort the slab_info linked list based on sort_func */ static struct slab_info *slabsort(struct slab_info *list) { struct slab_info *a, *b; if ((list == NULL) || (list->next == NULL)) return list; a = list; b = list->next; while ((b != NULL) && (b->next != NULL)) { list = list->next; b = b->next->next; } b = list->next; list->next = NULL; return merge_objs(slabsort(a), slabsort(b)); } /* * Sort Routines. Each of these should be associated with a command-line * search option. The functions should fit the prototype: * * int sort_foo(const struct slab_info *a, const struct slab_info *b) * * They return one if the first parameter is larger than the second * Otherwise, they return zero. */ static int sort_name(const struct slab_info *a, const struct slab_info *b) { return (strcmp(a->name, b->name) < 0) ? 1 : 0; } static int sort_nr_objs(const struct slab_info *a, const struct slab_info *b) { return (a->nr_objs > b->nr_objs); } static int sort_nr_active_objs(const struct slab_info *a, const struct slab_info *b) { return (a->nr_active_objs > b->nr_active_objs); } static int sort_obj_size(const struct slab_info *a, const struct slab_info *b) { return (a->obj_size > b->obj_size); } static int sort_objs_per_slab(const struct slab_info *a, const struct slab_info *b) { return (a->objs_per_slab > b->objs_per_slab); } static int sort_pages_per_slab(const struct slab_info *a, const struct slab_info *b) { return (a->pages_per_slab > b->pages_per_slab); } static int sort_nr_slabs(const struct slab_info *a, const struct slab_info *b) { return (a->nr_slabs > b->nr_slabs); } static int sort_nr_active_slabs(const struct slab_info *a, const struct slab_info *b) { return (a->nr_active_slabs > b->nr_active_slabs); } static int sort_use(const struct slab_info *a, const struct slab_info *b) { return (a->use > b->use); } static int sort_cache_size(const struct slab_info *a, const struct slab_info *b) { return (a->cache_size > b->cache_size); } /* * term_size - set the globals 'cols' and 'rows' to the current terminal size */ static void term_size(int unused) { struct winsize ws; (void) unused; if ((ioctl(1, 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) { (void) unused; delay = 0; } static void usage(const char *cmd) { fprintf(stderr, "usage: %s [options]\n\n", cmd); fprintf(stderr, "options:\n"); fprintf(stderr, " --delay=n, -d n " "delay n seconds between updates\n"); fprintf(stderr, " --once, -o " "only display once, then exit\n"); fprintf(stderr, " --sort=S, -s S " "specify sort criteria S (see below)\n"); fprintf(stderr, " --version, -V " "display version information and exit\n"); fprintf(stderr, " --help display this help and exit\n\n"); fprintf(stderr, "The following are valid sort criteria:\n"); fprintf(stderr, " a: sort by number of active objects\n"); fprintf(stderr, " b: sort by objects per slab\n"); fprintf(stderr, " c: sort by cache size\n"); fprintf(stderr, " l: sort by number of slabs\n"); fprintf(stderr, " v: sort by number of active slabs\n"); fprintf(stderr, " n: sort by name\n"); fprintf(stderr, " o: sort by number of objects\n"); fprintf(stderr, " p: sort by pages per slab\n"); fprintf(stderr, " s: sort by object size\n"); fprintf(stderr, " u: sort by cache utilization\n"); } /* * set_sort_func - return the slab_sort_func that matches the given key. * On unrecognizable key, DEF_SORT_FUNC is returned. */ static void * set_sort_func(char key) { switch (key) { case 'n': return sort_name; case 'o': return sort_nr_objs; case 'a': return sort_nr_active_objs; case 's': return sort_obj_size; case 'b': return sort_objs_per_slab; case 'p': return sort_pages_per_slab; case 'l': return sort_nr_slabs; case 'v': return sort_nr_active_slabs; case 'c': return sort_cache_size; case 'u': return sort_use; default: return DEF_SORT_FUNC; } } static void parse_input(char c) { c = toupper(c); switch(c) { case 'A': sort_func = sort_nr_active_objs; break; case 'B': sort_func = sort_objs_per_slab; break; case 'C': sort_func = sort_cache_size; break; case 'L': sort_func = sort_nr_slabs; break; case 'V': sort_func = sort_nr_active_slabs; break; case 'N': sort_func = sort_name; break; case 'O': sort_func = sort_nr_objs; break; case 'P': sort_func = sort_pages_per_slab; break; case 'S': sort_func = sort_obj_size; break; case 'U': sort_func = sort_use; break; case 'Q': delay = 0; break; } } #define print_line(fmt, args...) if (run_once) printf(fmt, ## args); else printw(fmt, ## args) int main(int argc, char *argv[]) { int o; unsigned short old_rows; struct slab_info *slab_list = NULL; int run_once=0; struct option longopts[] = { { "delay", 1, NULL, 'd' }, { "sort", 1, NULL, 's' }, { "once", 0, NULL, 'o' }, { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, { NULL, 0, NULL, 0 } }; sort_func = DEF_SORT_FUNC; while ((o = getopt_long(argc, argv, "d:s:ohV", longopts, NULL)) != -1) { int ret = 1; switch (o) { case 'd': errno = 0; delay = strtol(optarg, NULL, 10); if (errno) { perror("strtoul"); return 1; } if (delay < 0) { fprintf(stderr, "error: can't have a "\ "negative delay\n"); exit(1); } break; case 's': sort_func = set_sort_func(optarg[0]); break; case 'o': run_once=1; delay = 0; break; case 'V': display_version(); return 0; case 'h': ret = 0; default: usage(argv[0]); return ret; } } if (tcgetattr(0, &saved_tty) == -1) perror("tcgetattr"); old_rows = rows; term_size(0); if (!run_once) { initscr(); resizeterm(rows, cols); signal(SIGWINCH, term_size); } signal(SIGINT, sigint_handler); do { struct slab_info *curr; struct slab_stat stats = SLAB_STAT_ZERO; struct timeval tv; fd_set readfds; char c; int i; if (get_slabinfo(&slab_list, &stats)) break; if (!run_once && old_rows != rows) { resizeterm(rows, cols); old_rows = rows; } move(0,0); print_line( " Active / Total Objects (%% used) : %d / %d (%.1f%%)\n" " Active / Total Slabs (%% used) : %d / %d (%.1f%%)\n" " Active / Total Caches (%% used) : %d / %d (%.1f%%)\n" " Active / Total Size (%% used) : %.2fK / %.2fK (%.1f%%)\n" " Minimum / Average / Maximum Object : %.2fK / %.2fK / %.2fK\n\n", stats.nr_active_objs, stats.nr_objs, 100.0 * stats.nr_active_objs / stats.nr_objs, stats.nr_active_slabs, stats.nr_slabs, 100.0 * stats.nr_active_slabs / stats.nr_slabs, stats.nr_active_caches, stats.nr_caches, 100.0 * stats.nr_active_caches / stats.nr_caches, stats.active_size / 1024.0, stats.total_size / 1024.0, 100.0 * stats.active_size / stats.total_size, stats.min_obj_size / 1024.0, stats.avg_obj_size / 1024.0, stats.max_obj_size / 1024.0 ); slab_list = slabsort(slab_list); attron(A_REVERSE); print_line( "%6s %6s %4s %8s %6s %8s %10s %-23s\n", "OBJS", "ACTIVE", "USE", "OBJ SIZE", "SLABS", "OBJ/SLAB", "CACHE SIZE", "NAME"); attroff(A_REVERSE); curr = slab_list; for (i = 0; i < rows - 8 && curr->next; i++) { print_line("%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n", curr->nr_objs, curr->nr_active_objs, curr->use, curr->obj_size / 1024.0, curr->nr_slabs, curr->objs_per_slab, (unsigned)(curr->cache_size / 1024), curr->name); curr = curr->next; } put_slabinfo(slab_list); if (!run_once) { refresh(); FD_ZERO(&readfds); FD_SET(0, &readfds); tv.tv_sec = delay; tv.tv_usec = 0; if (select(1, &readfds, NULL, NULL, &tv) > 0) { if (read(0, &c, 1) != 1) break; parse_input(c); } } } while (delay); tcsetattr(0, TCSAFLUSH, &saved_tty); free_slabinfo(slab_list); if (!run_once) endwin(); return 0; }