]> granicus.if.org Git - procps-ng/commitdiff
add slabtop, fix top ^Z terminal handling
authoralbert <>
Sat, 24 Jan 2004 22:33:56 +0000 (22:33 +0000)
committeralbert <>
Sat, 24 Jan 2004 22:33:56 +0000 (22:33 +0000)
14 files changed:
Makefile
NEWS
README
proc/devname.c
proc/library.map
proc/procps.h
proc/slab.c [new file with mode: 0644]
proc/slab.h [new file with mode: 0644]
procps.lsm
procps.spec
ps/output.c
slabtop.1 [new file with mode: 0644]
slabtop.c [new file with mode: 0644]
top.c

index 9df642ee188861889bb6fc84e3034a4e154416e7..687e15673f8b8ae27054228becadd7bd8aabd2ac 100644 (file)
--- 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 94902e32ddac9a7c16b277d7033d5e6cc41b649f..13753478fc29e6b10093c516f5faaef4ba66481e 100644 (file)
--- 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 de5915614066c0ad78a7a944c6da1e4db5ca4603..77e40062de78b206ff433bb2ade751590440f392 100644 (file)
--- 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:
index c22c5580d22319ded898af7fe333a7b57ffd412c..78c00cd354a5652377e8aef1ae60bc2bb76a3db1 100644 (file)
@@ -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 -->
index 3440e3ad250f4a70d2112cddcdefa351268feac8..27a10315fa3f2d09b90dc8fc46a054e7e6740ce3 100644 (file)
@@ -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: *;
 };
index 5909c20aac80a3e51f7750c6ce66648dbc7022ac..95583c3f1e8f3192bcc3ff1bde4534f22afa99c2 100644 (file)
@@ -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 (file)
index 0000000..3e505d3
--- /dev/null
@@ -0,0 +1,290 @@
+/* 
+ * slab.c - slab related functions for libproc
+ *
+ * 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 <limits.h>
+
+#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 (file)
index 0000000..ce0858d
--- /dev/null
@@ -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 */
index d6a85bea947f59beb1f2cb09e14a4eba043194cc..52ebd7e755cba7cb815b92b7faf2502f77e5b5f3 100644 (file)
@@ -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 <procps-feedback@lists.sf.net>
 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
index cecc3349d982a0405557d2f27c76e205d92e1e15..f5d5e75dcb90e77974cdd8b6a6ed536bdd6ab74b 100644 (file)
@@ -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
index 5add745877f283cfb4bf3c9dfe82aee4ae8a1a5d..6d73caa3c93d164b7dfbeecb71200f01735cf019 100644 (file)
@@ -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 (file)
index 0000000..aa8b0a7
--- /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 <SPACEBAR>
+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 <procps-list@redhat.com>.
diff --git a/slabtop.c b/slabtop.c
new file mode 100644 (file)
index 0000000..9cc2e35
--- /dev/null
+++ b/slabtop.c
@@ -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;
+}
diff --git a/top.c b/top.c
index 93bab669a62272148fc3cf5248d58b768894c8ea..2446d04ec50a2c38d7e79cc175e1514344fa42c2 100644 (file)
--- 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);
 }
 
 \f