ce61089059
Add license header to all files. The summary of licensing is below, taken from Craig Small's email which is referred in commit message tail. sysctl and pgrep are GPL 2+ The rest is LGPL 2.1+ Reference: http://www.freelists.org/post/procps/Incorrect-FSF-address-in-the-license-files,8 Bug-Redhat: https://bugzilla.redhat.com/show_bug.cgi?id=797962 CC: Craig Small <csmall@enc.com.au> CC: Jaromir Capik <jcapik@redhat.com> Signed-off-by: Sami Kerola <kerolasa@iki.fi>
430 lines
10 KiB
C
430 lines
10 KiB
C
/*
|
|
* slabtop.c - utility to display kernel slab information.
|
|
*
|
|
* Chris Rivera <cmrivera@ufl.edu>
|
|
* Robert Love <rml@tech9.net>
|
|
*
|
|
* Copyright (C) 2003 Chris Rivera
|
|
*
|
|
* 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 <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 <sys/select.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include "c.h"
|
|
#include "nls.h"
|
|
#include "strutils.h"
|
|
#include "proc/slab.h"
|
|
#include "proc/version.h"
|
|
|
|
#define DEF_SORT_FUNC sort_nr_objs
|
|
|
|
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 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"
|
|
" -o, --once only display once, then exit\n"
|
|
" -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"
|
|
" a: sort by number of active objects\n"
|
|
" b: sort by objects per slab\n"
|
|
" c: sort by cache size\n"
|
|
" l: sort by number of slabs\n"
|
|
" v: sort by number of active slabs\n"
|
|
" n: sort by name\n"
|
|
" o: sort by number of objects (the default)\n"
|
|
" p: sort by pages per slab\n"
|
|
" s: sort by object size\n"
|
|
" u: sort by cache utilization\n"), out);
|
|
fprintf(out, USAGE_MAN_TAIL("slabtop(1)"));
|
|
|
|
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* 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 (void *) sort_name;
|
|
case 'o':
|
|
return (void *) sort_nr_objs;
|
|
case 'a':
|
|
return (void *) sort_nr_active_objs;
|
|
case 's':
|
|
return (void *) sort_obj_size;
|
|
case 'b':
|
|
return (void *) sort_objs_per_slab;
|
|
case 'p':
|
|
return (void *) sort_pages_per_slab;
|
|
case 'l':
|
|
return (void *) sort_nr_slabs;
|
|
case 'v':
|
|
return (void *) sort_nr_active_slabs;
|
|
case 'c':
|
|
return (void *) sort_cache_size;
|
|
case 'u':
|
|
return (void *) sort_use;
|
|
default:
|
|
return (void *) 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, ...) if (run_once) printf(fmt, __VA_ARGS__); else printw(fmt, __VA_ARGS__)
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int o;
|
|
unsigned short old_rows;
|
|
struct slab_info *slab_list = NULL;
|
|
int run_once = 0, retval = EXIT_SUCCESS;
|
|
|
|
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 }
|
|
};
|
|
|
|
program_invocation_name = program_invocation_short_name;
|
|
setlocale (LC_ALL, "");
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
textdomain(PACKAGE);
|
|
|
|
sort_func = DEF_SORT_FUNC;
|
|
|
|
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_func = (int (*)(const struct slab_info*,
|
|
const struct slab_info *)) set_sort_func(optarg[0]);
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (tcgetattr(STDIN_FILENO, &saved_tty) == -1)
|
|
xwarn(_("terminal setting retrieval"));
|
|
|
|
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;
|
|
struct timeval tv;
|
|
fd_set readfds;
|
|
char c;
|
|
int i;
|
|
memset(&stats, 0, sizeof(struct slab_stat));
|
|
|
|
if (get_slabinfo(&slab_list, &stats)) {
|
|
retval = EXIT_FAILURE;
|
|
break;
|
|
}
|
|
|
|
if (!run_once && old_rows != rows) {
|
|
resizeterm(rows, cols);
|
|
old_rows = rows;
|
|
}
|
|
|
|
move(0, 0);
|
|
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. */
|
|
_("Active / Total Objects (% used)"),
|
|
stats.nr_active_objs, stats.nr_objs,
|
|
100.0 * stats.nr_active_objs / stats.nr_objs,
|
|
_("Active / Total Slabs (% used)"),
|
|
stats.nr_active_slabs, stats.nr_slabs,
|
|
100.0 * stats.nr_active_slabs / stats.nr_slabs,
|
|
_("Active / Total Caches (% used)"),
|
|
stats.nr_active_caches, stats.nr_caches,
|
|
100.0 * stats.nr_active_caches / stats.nr_caches,
|
|
_("Active / Total Size (% used)"),
|
|
stats.active_size / 1024.0, stats.total_size / 1024.0,
|
|
100.0 * stats.active_size / stats.total_size,
|
|
_("Minimum / Average / Maximum Object"),
|
|
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);
|
|
/* Translation Hint: Please keep alignment of the
|
|
* following intact. */
|
|
print_line("%-78s\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(STDIN_FILENO, &readfds);
|
|
tv.tv_sec = delay;
|
|
tv.tv_usec = 0;
|
|
if (select(STDOUT_FILENO, &readfds, NULL, NULL, &tv) > 0) {
|
|
if (read(0, &c, 1) != 1)
|
|
break;
|
|
parse_input(c);
|
|
}
|
|
}
|
|
} while (delay);
|
|
|
|
tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty);
|
|
free_slabinfo(slab_list);
|
|
if (!run_once)
|
|
endwin();
|
|
return retval;
|
|
}
|