From efd8648fc75cf11b2196218e18279dda63966746 Mon Sep 17 00:00:00 2001 From: albert <> Date: Sat, 24 Jan 2004 22:33:56 +0000 Subject: [PATCH] add slabtop, fix top ^Z terminal handling --- Makefile | 26 ++- NEWS | 5 + README | 2 +- proc/devname.c | 2 +- proc/library.map | 3 +- proc/procps.h | 2 + proc/slab.c | 290 ++++++++++++++++++++++++++++++++++ proc/slab.h | 39 +++++ procps.lsm | 8 +- procps.spec | 2 +- ps/output.c | 8 +- slabtop.1 | 122 ++++++++++++++ slabtop.c | 403 +++++++++++++++++++++++++++++++++++++++++++++++ top.c | 4 + 14 files changed, 896 insertions(+), 20 deletions(-) create mode 100644 proc/slab.c create mode 100644 proc/slab.h create mode 100644 slabtop.1 create mode 100644 slabtop.c diff --git a/Makefile b/Makefile index 9df642ee..687e1567 100644 --- a/Makefile +++ b/Makefile @@ -18,9 +18,9 @@ VERSION := 3 SUBVERSION := 1 -MINORVERSION := 14 -TARVERSION := 3.1.14 -LIBVERSION := 3.1.14 +MINORVERSION := 15 +TARVERSION := 3.1.15 +LIBVERSION := 3.1.15 ############ vars @@ -51,18 +51,22 @@ usr/include := $(DESTDIR)/usr/include/ BINFILES := $(usr/bin)uptime $(usr/bin)tload $(usr/bin)free $(usr/bin)w \ $(usr/bin)top $(usr/bin)vmstat $(usr/bin)watch $(usr/bin)skill \ $(usr/bin)snice $(bin)kill $(sbin)sysctl $(usr/bin)pmap \ - $(usr/proc/bin)pgrep $(usr/proc/bin)pkill + $(usr/proc/bin)pgrep $(usr/proc/bin)pkill $(usr/bin)slabtop MANFILES := $(man1)uptime.1 $(man1)tload.1 $(man1)free.1 $(man1)w.1 \ $(man1)top.1 $(man1)watch.1 $(man1)skill.1 $(man1)kill.1 \ $(man1)snice.1 $(man1)pgrep.1 $(man1)pkill.1 $(man1)pmap.1 \ - $(man5)sysctl.conf.5 $(man8)vmstat.8 $(man8)sysctl.8 + $(man5)sysctl.conf.5 $(man8)vmstat.8 $(man8)sysctl.8 \ + $(man1)slabtop.1 TARFILES := AUTHORS BUGS NEWS README TODO COPYING COPYING.LIB \ Makefile procps.lsm procps.spec v t README.top \ minimal.c $(notdir $(MANFILES)) \ uptime.c tload.c free.c w.c top.c vmstat.c watch.c skill.c \ - sysctl.c pgrep.c top.h pmap.c + sysctl.c pgrep.c top.h pmap.c slabtop.c + +# Stuff (tests, temporary hacks, etc.) left out of the standard tarball +_TARFILES := CURSES := -I/usr/include/ncurses -lncurses @@ -98,7 +102,7 @@ ALL_LDFLAGS := $(PKG_LDFLAGS) $(LDFLAGS) .SUFFIXES: .SUFFIXES: .a .o .c .s .h -.PHONY: all clean do_all install tar # ps +.PHONY: all clean do_all install tar extratar ALL := $(notdir $(BINFILES)) @@ -141,6 +145,12 @@ tar: $(TARFILES) tar cf procps-$(TARVERSION).tar procps-$(TARVERSION) gzip -9 procps-$(TARVERSION).tar +extratar: $(_TARFILES) + mkdir extra-$(TARVERSION) + (tar cf - $(_TARFILES)) | (cd extra-$(TARVERSION) && tar xf -) + tar cf extra-$(TARVERSION).tar extra-$(TARVERSION) + gzip -9 extra-$(TARVERSION).tar + clean: rm -f $(CLEAN) @@ -171,7 +181,7 @@ w.o: w.c pmap w uptime tload free sysctl vmstat utmp pgrep skill: % : %.o $(LIBPROC) $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $^ -top: % : %.o $(LIBPROC) +slabtop top: % : %.o $(LIBPROC) $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $^ $(CURSES) watch: % : %.o diff --git a/NEWS b/NEWS index 94902e32..13753478 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,8 @@ +procps-3.1.15 --> procps-3.1.16 + +future-proof the tty handling (thanks to Zhou Wei) +slabtop (Chris Rivera and Robert Love) #226778 rh114012a + procps-3.1.14 --> procps-3.1.15 install to /lib64 if it exists diff --git a/README b/README index de591561..77e40062 100644 --- a/README +++ b/README @@ -29,7 +29,7 @@ INSTALLATION Suppose you wanted to install stuff in strange places. You might do something like this: - make usr/bin=/tmp/fff/iii/ DESTDIR=/tmp/fff install="install -D" ldconfig=echo install + make usr/bin=/tmp/Q/iii/ DESTDIR=/tmp/Q install="install -D" ldconfig=echo install If cross-compiling, you might need to set lib64 to either "lib" or "lib64", like one of these examples: diff --git a/proc/devname.c b/proc/devname.c index c22c5580..78c00cd3 100644 --- a/proc/devname.c +++ b/proc/devname.c @@ -36,7 +36,7 @@ #define MINOR_OF(d) ((unsigned)minor(d)) #else #define MAJOR_OF(d) ( ((unsigned)(d)>>8u) & 0xfffu ) -#define MINOR_OF(d) ( ((unsigned)(d)&0xffu) | (((unsigned)(d)&0xfff00000u)>>20u) ) +#define MINOR_OF(d) ( ((unsigned)(d)&0xffu) | (((unsigned)(d)&0xfff00000u)>>12u) ) #undef major #undef minor #define major <-- do not use --> diff --git a/proc/library.map b/proc/library.map index 3440e3ad..27a10315 100644 --- a/proc/library.map +++ b/proc/library.map @@ -14,6 +14,7 @@ global: kb_active; kb_inactive; kb_main_buffers; kb_main_cached; kb_main_free; kb_main_total; kb_main_used; kb_swap_free; kb_swap_total; kb_swap_used; kb_main_shared; - vm_pgpgin; vm_pgpgout; vm_pswpin; vm_pswpout; + vm_pgpgin; vm_pgpgout; vm_pswpin; vm_pswpout; free_slabinfo; put_slabinfo; + get_slabinfo; local: *; }; diff --git a/proc/procps.h b/proc/procps.h index 5909c20a..95583c3f 100644 --- a/proc/procps.h +++ b/proc/procps.h @@ -68,6 +68,8 @@ #define LABEL_OFFSET #endif +#define STRINGIFY_ARG(a) #a +#define STRINGIFY(a) STRINGIFY_ARG(a) // marks old junk, to warn non-procps library users #if ( __GNUC__ == 3 && __GNUC_MINOR__ > 0 ) || __GNUC__ > 3 diff --git a/proc/slab.c b/proc/slab.c new file mode 100644 index 00000000..3e505d36 --- /dev/null +++ b/proc/slab.c @@ -0,0 +1,290 @@ +/* + * slab.c - slab related functions for libproc + * + * 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 "slab.h" +#include "procps.h" + +#define SLABINFO_LINE_LEN 2048 +#define SLABINFO_VER_LEN 100 +#define SLABINFO_FILE "/proc/slabinfo" + +static struct slab_info *free_index; + +/* + * get_slabnode - allocate slab_info structures using a free list + * + * In the fast path, we simply return a node off the free list. In the slow + * list, we malloc() a new node. The free list is never automatically reaped, + * both for simplicity and because the number of slab caches is fairly + * constant. + */ +static struct slab_info *get_slabnode(void) +{ + struct slab_info *node; + + if (free_index) { + node = free_index; + free_index = free_index->next; + } else { + node = malloc(sizeof(struct slab_info)); + if (!node) + perror("malloc"); + } + + return node; +} + +/* + * put_slabinfo - return all allocated nodes to the free list + */ +void put_slabinfo(struct slab_info *head) +{ + free_index = head; +} + +/* + * free_slabinfo - deallocate the memory associated with each node in the + * slab_info linked list + */ +void free_slabinfo(struct slab_info *list) +{ + while (list) { + struct slab_info *temp = list->next; + free(list); + list = temp; + } +} + +/* + * parse_slabinfo20 - actual parse routine for slabinfo 2.0 (2.6 kernels) + */ +static int parse_slabinfo20(struct slab_info **list, struct slab_stat *stats, + FILE *f) +{ + struct slab_info *curr = NULL, *prev = NULL; + char buffer[SLABINFO_LINE_LEN]; + int entries = 0; + int page_size = getpagesize(); + + stats->min_obj_size = INT_MAX; + stats->max_obj_size = 0; + + while (fgets(buffer, SLABINFO_LINE_LEN, f)) { + int assigned; + + if (buffer[0] == '#') + continue; + + curr = get_slabnode(); + if (!curr) + break; + + if (entries++ == 0) + *list = curr; + else + prev->next = curr; + + assigned = sscanf(buffer, "%" STRINGIFY(SLAB_INFO_NAME_LEN) + "s %d %d %d %d %d : tunables %*d %*d %*d : \ + slabdata %d %d %*d", curr->name, + &curr->nr_active_objs, &curr->nr_objs, + &curr->obj_size, &curr->objs_per_slab, + &curr->pages_per_slab, &curr->nr_active_slabs, + &curr->nr_slabs); + + if (assigned < 8) { + fprintf(stderr, "unrecognizable data in slabinfo!\n"); + curr = NULL; + break; + } + + if (curr->obj_size < stats->min_obj_size) + stats->min_obj_size = curr->obj_size; + if (curr->obj_size > stats->max_obj_size) + stats->max_obj_size = curr->obj_size; + + curr->cache_size = curr->nr_slabs * curr->pages_per_slab * + page_size; + + if (curr->nr_objs) { + curr->use = 100 * curr->nr_active_objs / curr->nr_objs; + stats->nr_active_caches++; + } else + curr->use = 0; + + stats->nr_objs += curr->nr_objs; + stats->nr_active_objs += curr->nr_active_objs; + stats->total_size += curr->nr_objs * curr->obj_size; + stats->active_size += curr->nr_active_objs * curr->obj_size; + stats->nr_pages += curr->nr_slabs * curr->pages_per_slab; + stats->nr_slabs += curr->nr_slabs; + stats->nr_active_slabs += curr->nr_active_slabs; + + prev = curr; + } + + if (!curr) { + fprintf(stderr, "error reading slabinfo!\n"); + return 1; + } + + curr->next = NULL; + stats->nr_caches = entries; + if (stats->nr_objs) + stats->avg_obj_size = stats->total_size / stats->nr_objs; + + return 0; +} + +/* + * parse_slabinfo11 - actual parsing routine for slabinfo 1.1 (2.4 kernels) + */ +static int parse_slabinfo11(struct slab_info **list, struct slab_stat *stats, + FILE *f) +{ + struct slab_info *curr = NULL, *prev = NULL; + char buffer[SLABINFO_LINE_LEN]; + int entries = 0; + int page_size = getpagesize(); + + stats->min_obj_size = INT_MAX; + stats->max_obj_size = 0; + + while (fgets(buffer, SLABINFO_LINE_LEN, f)) { + int assigned; + + curr = get_slabnode(); + if (!curr) + break; + + if (entries++ == 0) + *list = curr; + else + prev->next = curr; + + assigned = sscanf(buffer, "%" STRINGIFY(SLAB_INFO_NAME_LEN) + "s %d %d %d %d %d %d", + curr->name, &curr->nr_active_objs, + &curr->nr_objs, &curr->obj_size, + &curr->nr_active_slabs, &curr->nr_slabs, + &curr->pages_per_slab); + + if (assigned < 6) { + fprintf(stderr, "unrecognizable data in slabinfo!\n"); + curr = NULL; + break; + } + + if (curr->obj_size < stats->min_obj_size) + stats->min_obj_size = curr->obj_size; + if (curr->obj_size > stats->max_obj_size) + stats->max_obj_size = curr->obj_size; + + curr->cache_size = curr->nr_slabs * curr->pages_per_slab * + page_size; + + if (curr->nr_objs) { + curr->use = 100 * curr->nr_active_objs / curr->nr_objs; + stats->nr_active_caches++; + } else + curr->use = 0; + + if (curr->obj_size) + curr->objs_per_slab = curr->pages_per_slab * + page_size / curr->obj_size; + + stats->nr_objs += curr->nr_objs; + stats->nr_active_objs += curr->nr_active_objs; + stats->total_size += curr->nr_objs * curr->obj_size; + stats->active_size += curr->nr_active_objs * curr->obj_size; + stats->nr_pages += curr->nr_slabs * curr->pages_per_slab; + stats->nr_slabs += curr->nr_slabs; + stats->nr_active_slabs += curr->nr_active_slabs; + + prev = curr; + } + + if (!curr) { + fprintf(stderr, "error reading slabinfo!\n"); + return 1; + } + + curr->next = NULL; + stats->nr_caches = entries; + if (stats->nr_objs) + stats->avg_obj_size = stats->total_size / stats->nr_objs; + + return 0; +} + +/* + * parse_slabinfo10 - actual parsing routine for slabinfo 1.0 (2.2 kernels) + * + * Not yet implemented. Please feel free. + */ +static int parse_slabinfo10(struct slab_info **list, struct slab_stat *stats, + FILE *f) +{ + (void) list, (void) stats, (void) f; + fprintf(stderr, "slabinfo version 1.0 not yet supported\n"); + return 1; +} + +/* + * slabinfo - parse the system's slabinfo and fill out both a linked list of + * slab_info structures and the slab_stat structure + * + * The function returns zero on success, in which case 'list' and 'stats' are + * valid. Nonzero is returned on failure and the state of 'list' and 'stats' + * are undefined. + */ +int get_slabinfo(struct slab_info **list, struct slab_stat *stats) +{ + FILE *slabfile; + char buffer[SLABINFO_VER_LEN]; + int major, minor, ret = 0; + + slabfile = fopen(SLABINFO_FILE, "r"); + if (!slabfile) { + perror("fopen"); + return 1; + } + + if (!fgets(buffer, SLABINFO_VER_LEN, slabfile)) { + fprintf(stderr, "cannot read from slabinfo\n"); + return 1; + } + + if (sscanf(buffer, "slabinfo - version: %d.%d", &major, &minor) != 2) { + fprintf(stderr, "not the good old slabinfo we know\n"); + return 1; + } + + if (major == 2 && minor == 0) + ret = parse_slabinfo20(list, stats, slabfile); + else if (major == 1 && minor == 1) + ret = parse_slabinfo11(list, stats, slabfile); + else if (major == 1 && minor == 0) + ret = parse_slabinfo10(list, stats, slabfile); + else { + fprintf(stderr, "unrecognizable slabinfo version\n"); + return 1; + } + + fclose(slabfile); + + return ret; +} diff --git a/proc/slab.h b/proc/slab.h new file mode 100644 index 00000000..ce0858d8 --- /dev/null +++ b/proc/slab.h @@ -0,0 +1,39 @@ +#ifndef _PROC_SLAB_H +#define _PROC_SLAB_H + +#define SLAB_INFO_NAME_LEN 64 + +struct slab_info { + char name[SLAB_INFO_NAME_LEN]; /* name of this cache */ + int nr_objs; /* number of objects in this cache */ + int nr_active_objs; /* number of active objects */ + int obj_size; /* size of each object */ + int objs_per_slab; /* number of objects per slab */ + int pages_per_slab; /* number of pages per slab */ + int nr_slabs; /* number of slabs in this cache */ + int nr_active_slabs; /* number of active slabs */ + int use; /* percent full: total / active */ + int cache_size; /* size of entire cache */ + struct slab_info *next; +}; + +struct slab_stat { + int nr_objs; /* number of objects, among all caches */ + int nr_active_objs; /* number of active objects, among all caches */ + int total_size; /* size of all objects */ + int active_size; /* size of all active objects */ + int nr_pages; /* number of pages consumed by all objects */ + int nr_slabs; /* number of slabs, among all caches */ + int nr_active_slabs; /* number of active slabs, among all caches */ + int nr_caches; /* number of caches */ + int nr_active_caches; /* number of active caches */ + int avg_obj_size; /* average object size */ + int min_obj_size; /* size of smallest object */ + int max_obj_size; /* size of largest object */ +}; + +extern void put_slabinfo(struct slab_info *); +extern void free_slabinfo(struct slab_info *); +extern int get_slabinfo(struct slab_info **, struct slab_stat *); + +#endif /* _PROC_SLAB_H */ diff --git a/procps.lsm b/procps.lsm index d6a85bea..52ebd7e7 100644 --- a/procps.lsm +++ b/procps.lsm @@ -1,15 +1,15 @@ Begin4 Title: procps -Version: 3.1.14 -Entered-date: 2003-09-26 +Version: 3.1.15 +Entered-date: 2003-12-24 Description: Linux system utilities Keywords: procps /proc libproc sysctl pmap ps uptime tload free w top vmstat watch skill snice kill pgrep pkill Author: Albert Cahalan, Michael K. Johnson, Jim Warner, etc. Maintained-by: various Primary-site: http://procps.sf.net/ - 242kB procps-3.1.14.tar.gz + 242kB procps-3.1.15.tar.gz Alternate-site: http://www.debian.org/Packages/unstable/base/procps.html - 242kB procps-3.1.14.tar.gz + 242kB procps-3.1.15.tar.gz Copying-policy: mixed End diff --git a/procps.spec b/procps.spec index cecc3349..f5d5e75d 100644 --- a/procps.spec +++ b/procps.spec @@ -3,7 +3,7 @@ Summary: System and process monitoring utilities Name: procps %define major_version 3 %define minor_version 1 -%define revision 14 +%define revision 15 %define version %{major_version}.%{minor_version}.%{revision} Version: %{version} Release: 1 diff --git a/ps/output.c b/ps/output.c index 5add7458..6d73caa3 100644 --- a/ps/output.c +++ b/ps/output.c @@ -943,8 +943,8 @@ static int pr_context(char *restrict const outbuf, const proc_t *restrict const int fd; // wchan file is suitable for testing -//snprintf(filename, sizeof filename, "/proc/%d/task/%d/wchan", pp->tgid, pp->tid); - snprintf(filename, sizeof filename, "/proc/%d/task/%d/attr/current", pp->tgid, pp->tid); +//snprintf(filename, sizeof filename, "/proc/%d/wchan", pp->tgid); + snprintf(filename, sizeof filename, "/proc/%d/attr/current", pp->tgid); fd = open(filename, O_RDONLY, 0); if(likely(fd==-1)) goto fail; @@ -1052,7 +1052,7 @@ static const format_struct format_array[] = { {"cnswap", "-", pr_nop, sr_cnswap, 1, 0, LNX, AN|RIGHT}, {"comm", "COMMAND", pr_comm, sr_nop, 16, COM, U98, PO|UNLIMITED}, /*ucomm*/ {"command", "COMMAND", pr_args, sr_nop, 16, ARG, XXX, PO|UNLIMITED}, /*args*/ -{"context", "CONTEXT", pr_context, sr_nop, 40, 0, LNX, AN|LEFT}, +{"context", "CONTEXT", pr_context, sr_nop, 40, 0, LNX, PO|LEFT}, {"cp", "CP", pr_cp, sr_pcpu, 3, 0, DEC, ET|RIGHT}, /*cpu*/ {"cpu", "CPU", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */ {"cputime", "TIME", pr_time, sr_nop, 8, 0, DEC, ET|RIGHT}, /*time*/ @@ -1092,7 +1092,7 @@ static const format_struct format_array[] = { {"jobc", "JOBC", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT}, {"ktrace", "KTRACE", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT}, {"ktracep", "KTRACEP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT}, -{"label", "LABEL", pr_nop, sr_nop, 25, 0, SGI, AN|LEFT}, +{"label", "LABEL", pr_nop, sr_nop, 25, 0, SGI, PO|LEFT}, {"lim", "LIM", pr_lim, sr_rss_rlim, 5, 0, BSD, AN|RIGHT}, {"login", "LOGNAME", pr_nop, sr_nop, 8, 0, BSD, AN|LEFT}, /*logname*/ /* double check */ {"logname", "LOGNAME", pr_nop, sr_nop, 8, 0, XXX, AN|LEFT}, /*login*/ diff --git a/slabtop.1 b/slabtop.1 new file mode 100644 index 00000000..aa8b0a70 --- /dev/null +++ b/slabtop.1 @@ -0,0 +1,122 @@ +.\" slabtop.1 - manpage for the slabtop(1) utility, part of procps +.\" +.\" Copyright (C) 2003 Chris Rivera +.\" Licensed under the terms of the GNU Library General Public License, v2 +.TH SLABTOP 1 "13 Sep 2003" "Linux" "Linux User's Manual" +.SH NAME +slabtop \- display kernel slab cache information in real time + +.SH SYNOPSIS +.BI "slabtop [ " options " ] " + +.SH DESCRIPTION +.BR slabtop (1) +displays detailed kernel slab cache information in real time. It displays a +listing of the top caches sorted by one of the listed sort criterias. It also +displays a statistics header filled with slab layer information. + +.SH OPTIONS +Normal invocation of +.BR slabtop (1) +does not require any options. The behavior, however, can be fine-tuned by +specifying one or more of the following flags: +.TP +.B \-\^\-delay=n, \-d n +Refresh the display every n seconds. By default, +.BR slabtop (1) +refreshes the display every three seconds. To exit the program, hit +.BR q. +.TP +.B \-\^\-sort=S, \-s S +Sort by S, where S is one of the sort criteria. +.TP +.B \-\^\-once, \-o +Display the output once and then exit. +.TP +.B \-\^\-version, \-V +Display version information and exit. +.TP +.B \-\^\-help +Display usage information and exit. + +.SH SORT CRITERIA +The following are valid sort criteria used to sort the individual slab caches +and thereby determine what are the "top" slab caches to display. The default +sort criteria is to sort by the number of objects ("o"). + +The sort criteria can also be changed while slabtop is running by pressing +the associated character. +.TP +.BR a: +sort by number of active objects +.TP +.BR b: +sort by objects per slab +.TP +.BR c: +sort by cache size +.TP +.BR l: +sort by number of slabs +.TP +.BR v +sort by number of active slabs +.TP +.BR n: +sort by name +.TP +.BR o: +sort by number of objects +.TP +.BR p: +sort by pages per slab +.TP +.BR s: +sort by object size +.TP +.BR u: +sort by cache utilization + +.SH COMMANDS +.BR slabtop (1) +accepts keyboard commands from the user during use. The following are +supported. In the case of letters, both cases are accepted. + +Each of the valid sort characters are also accepted, to change the sort +routine. See the section +.IR "SORT CRITERIA" . + +.TP +.BR +Refresh the screen. +.TP +.BR Q +Quit the program. + +.SH FILES +.IR /proc/slabinfo " \-\- slab information" + +.SH "SEE ALSO" +.BR free (1), +.BR ps (1), +.BR top (1), +.BR vmstat (8) + +.SH NOTES +Currently, +.BR slabtop (1) +requires a 2.4 or later kernel (specifically, a version 1.1 or later +.IR /proc/slabinfo ). +Kernel 2.2 should be supported in the future. + +.SH AUTHORS +Written by Chris Rivera and Robert Love. + +.BR slabtop (1) +was inspired by Martin Bligh's perl script, +.BR vmtop . + +The procps package is maintained by Robert Love and was created by Michael +Johnson. + +Send bug reports to . diff --git a/slabtop.c b/slabtop.c new file mode 100644 index 00000000..9cc2e35f --- /dev/null +++ b/slabtop.c @@ -0,0 +1,403 @@ +/* + * 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 "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; +} diff --git a/top.c b/top.c index 93bab669..2446d04e 100644 --- a/top.c +++ b/top.c @@ -446,12 +446,16 @@ static void suspend (int dont_care_sig) tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); putp(tg2(0, Screen_rows)); putp(Cap_curs_norm); + putp(Cap_smam); + putp("\n"); fflush(stdout); raise(SIGSTOP); /* later, after SIGCONT... */ ZAP_TIMEOUT if (!Batch) tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty); + putp(Cap_clr_scr); + putp(Cap_rmam); }