add slabtop, fix top ^Z terminal handling
This commit is contained in:
403
slabtop.c
Normal file
403
slabtop.c
Normal file
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* slabtop.c - utility to display kernel slab information.
|
||||
*
|
||||
* Chris Rivera <cmrivera@ufl.edu>
|
||||
* Robert Love <rml@tech9.net>
|
||||
*
|
||||
* This program is licensed under the GNU Library General Public License, v2
|
||||
*
|
||||
* Copyright (C) 2003 Chris Rivera
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.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 <sys/select.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int o;
|
||||
unsigned short old_rows;
|
||||
struct slab_info *slab_list = NULL;
|
||||
|
||||
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':
|
||||
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");
|
||||
|
||||
initscr();
|
||||
term_size(0);
|
||||
old_rows = rows;
|
||||
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 (old_rows != rows) {
|
||||
resizeterm(rows, cols);
|
||||
old_rows = rows;
|
||||
}
|
||||
|
||||
move(0,0);
|
||||
printw( " 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);
|
||||
printw( "%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++) {
|
||||
printw("%6d %6d %3d%% %7.2fK %6d %8d %9dK %-23s\n",
|
||||
curr->nr_objs, curr->nr_active_objs, curr->use,
|
||||
curr->obj_size / 1024.0, curr->nr_slabs,
|
||||
curr->objs_per_slab, curr->cache_size / 1024,
|
||||
curr->name);
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
refresh();
|
||||
put_slabinfo(slab_list);
|
||||
|
||||
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);
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user