diff options
Diffstat (limited to 'showslab/showslab.c')
-rw-r--r-- | showslab/showslab.c | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/showslab/showslab.c b/showslab/showslab.c new file mode 100644 index 00000000..9c9e60eb --- /dev/null +++ b/showslab/showslab.c @@ -0,0 +1,351 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <ctype.h> +#include <limits.h> + +#define STRINGIFY_ARG(a) #a +#define STRINGIFY(a) STRINGIFY_ARG(a) + +#define DEF_SORT_FUNC sort_nr_objs +#define SLABINFO_LINE_LEN 512 /* size of longest line */ +#define SLABINFO_NAME_LEN 32 /* cache name size (will truncate) */ +#define SLABINFO_FILE "/proc/slabinfo" +#define DEF_NR_ROWS 15 /* default nr of caches to show */ + +/* object representing a slab cache (each line of slabinfo) */ +struct slab_info { + char name[SLABINFO_NAME_LEN]; /* name of this cache */ + struct slab_info *next; + unsigned long nr_pages; /* size of cache in pages */ + unsigned long nr_objs; /* number of objects in this cache */ + unsigned long nr_active_objs; /* number of active objects */ + unsigned long obj_size; /* size of each object */ + unsigned long objs_per_slab; /* number of objects per slab */ + unsigned long nr_slabs; /* number of slabs in this cache */ + unsigned long use; /* percent full: total / active */ +}; + +/* object representing system-wide statistics */ +struct slab_stat { + unsigned long total_size; /* size of all objects */ + unsigned long active_size; /* size of all active objects */ + unsigned long nr_objs; /* total number of objects */ + unsigned long nr_active_objs; /* total number of active objects */ + unsigned long nr_slabs; /* total number of slabs */ + unsigned long nr_active_slabs; /* total number of active slabs*/ + unsigned long nr_caches; /* number of caches */ + unsigned long nr_active_caches; /* number of active caches */ + unsigned long avg_obj_size; /* average object size */ + unsigned long min_obj_size; /* size of smallest object */ + unsigned long max_obj_size; /* size of largest object */ +}; + +typedef int (*sort_t)(const struct slab_info *, const struct slab_info *); +static sort_t sort_func; + +/* + * get_slabinfo - open, read, and parse a slabinfo 2.x file, which has the + * following format: + * + * slabinfo - version: 2.1 + * <name> <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> + * : tunables <limit> <batchcount> <sharedfactor> + * : slabdata <active_slabs> <num_slabs> <sharedavail> + * + * Returns the head of the new list of slab_info structures, or NULL on error. + */ +static struct slab_info * get_slabinfo(struct slab_stat *stats) +{ + struct slab_info *head = NULL, *p = NULL, *prev = NULL; + FILE *slabfile; + char line[SLABINFO_LINE_LEN]; + unsigned int major, minor; + + slabfile = fopen(SLABINFO_FILE, "r"); + if (!slabfile) { + perror("fopen"); + return NULL; + } + + if (!fgets(line, SLABINFO_LINE_LEN, slabfile)) { + fprintf(stderr, "cannot read from " SLABINFO_FILE "\n"); + return NULL; + } + + if (sscanf(line, "slabinfo - version: %u.%u", &major, &minor) != 2) { + fprintf(stderr, "unable to parse slabinfo version!\n"); + return NULL; + } + + if (major != 2 || minor > 1) { + fprintf(stderr, "we only support slabinfo 2.0 and 2.1!\n"); + return NULL; + } + + stats->min_obj_size = INT_MAX; + + while (fgets(line, SLABINFO_LINE_LEN, slabfile)) { + unsigned long nr_active_slabs, pages_per_slab; + int ret; + + if (line[0] == '#') + continue; + + p = malloc(sizeof (struct slab_info)); + if (!p) { + perror("malloc"); + head = NULL; + break; + } + if (stats->nr_caches++ == 0) + head = prev = p; + + ret = sscanf(line, "%" STRINGIFY(SLABINFO_NAME_LEN) "s" + " %lu %lu %lu %lu %lu : tunables %*d %*d %*d : \ + slabdata %lu %lu %*d", p->name, + &p->nr_active_objs, &p->nr_objs, + &p->obj_size, &p->objs_per_slab, + &pages_per_slab, + &nr_active_slabs, + &p->nr_slabs); + + if (ret != 8) { + fprintf(stderr, "unrecognizable data in slabinfo!\n"); + head = NULL; + break; + } + + if (p->obj_size < stats->min_obj_size) + stats->min_obj_size = p->obj_size; + if (p->obj_size > stats->max_obj_size) + stats->max_obj_size = p->obj_size; + + p->nr_pages = p->nr_slabs * pages_per_slab; + + if (p->nr_objs) { + p->use = 100 * p->nr_active_objs / p->nr_objs; + stats->nr_active_caches++; + } else + p->use = 0; + + stats->nr_objs += p->nr_objs; + stats->nr_active_objs += p->nr_active_objs; + stats->total_size += p->nr_objs * p->obj_size; + stats->active_size += p->nr_active_objs * p->obj_size; + stats->nr_slabs += p->nr_slabs; + stats->nr_active_slabs += nr_active_slabs; + + prev->next = p; + prev = p; + } + + if (fclose(slabfile)) + perror("fclose"); + + if (p) + p->next = NULL; + if (stats->nr_objs) + stats->avg_obj_size = stats->total_size / stats->nr_objs; + + return head; +} + +/* + * free_slablist - deallocate the memory associated with each node in the + * provided slab_info linked list + */ +static void free_slablist(struct slab_info *list) +{ + while (list) { + struct slab_info *temp = list->next; + free(list); + list = temp; + } +} + +static struct slab_info *merge_objs(struct slab_info *a, struct slab_info *b) +{ + struct slab_info list; + struct slab_info *p = &list; + + while (a && b) { + if (sort_func(a, b)) { + p->next = a; + p = a; + a = a->next; + } else { + p->next = b; + p = b; + b = b->next; + } + } + + p->next = (a == NULL) ? b : a; + return 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 || !list->next) + return list; + + a = list; + b = list->next; + + while (b && b->next) { + 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 zero if the first parameter is smaller than the second. + * Otherwise, they return nonzero. + */ + +static int sort_name(const struct slab_info *a, const struct slab_info *b) +{ + return (strcmp(a->name, b->name) < 0 ) ? 1: 0; +} + +#define BUILD_SORT_FUNC(VAL) \ + static int sort_ ## VAL \ + (const struct slab_info *a, const struct slab_info *b) { \ + return (a-> VAL > b-> VAL); } + +BUILD_SORT_FUNC(nr_objs) +BUILD_SORT_FUNC(nr_active_objs) +BUILD_SORT_FUNC(obj_size) +BUILD_SORT_FUNC(objs_per_slab) +BUILD_SORT_FUNC(nr_slabs) +BUILD_SORT_FUNC(use) +BUILD_SORT_FUNC(nr_pages) + +/* + * set_sort_func - return the slab_sort_func that matches the given key. + * On unrecognizable key, the call returns NULL. + */ +static void * set_sort_func(char key) +{ + switch (tolower(key)) { + case 'a': + return sort_nr_active_objs; + case 'c': + return sort_nr_pages; + case 'l': + return sort_nr_slabs; + case 'n': + return sort_name; + case 'o': + return sort_nr_objs; + case 'p': + return sort_objs_per_slab; + case 's': + return sort_obj_size; + case 'u': + return sort_use; + default: + return NULL; + } +} + +int main(int argc, char *argv[]) +{ + struct slab_info *list, *p; + struct slab_stat stats = { .nr_objs = 0 }; + unsigned int page_size = getpagesize() / 1024, nr_rows = DEF_NR_ROWS, i; + + sort_func = DEF_SORT_FUNC; + + if (argc > 1) { + /* FIXME: Ugh. */ + if (argc == 3 && !strcmp(argv[1], "-n")) { + errno = 0; + nr_rows = (unsigned int) strtoul(argv[2], NULL, 0); + if (errno) { + perror("strtoul"); + exit(EXIT_FAILURE); + } + } + else if (argc == 3 && !strcmp(argv[1], "-s")) + sort_func = set_sort_func(argv[2][0]) ? : DEF_SORT_FUNC; + else { + fprintf(stderr, "usage: %s [options]\n\n", argv[0]); + fprintf(stderr, "options:\n"); + fprintf(stderr, " -s S specify sort criteria S\n"); + fprintf(stderr, " -h display this help\n\n"); + fprintf(stderr, "Valid sort criteria:\n"); + fprintf(stderr, " a: number of Active objects\n"); + fprintf(stderr, " c: Cache size\n"); + fprintf(stderr, " l: number of sLabs\n"); + fprintf(stderr, " n: Name\n"); + fprintf(stderr, " o: number of Objects\n"); + fprintf(stderr, " p: objects Per slab\n"); + fprintf(stderr, " s: object Size\n"); + fprintf(stderr, " u: cache Utilization\n"); + exit(EXIT_FAILURE); + } + } + + list = get_slabinfo (&stats); + if (!list) + exit(EXIT_FAILURE); + + printf(" Active / Total Objects (%% used) : %lu / %lu (%.1f%%)\n" + " Active / Total Slabs (%% used) : %lu / %lu (%.1f%%)\n" + " Active / Total Caches (%% used) : %lu / %lu (%.1f%%)\n" + " Active / Total Size (%% used) : %.2fK / %.2fK (%.1f%%)\n" + " Min / Avg / Max Object Size : %.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); + + printf("%6s %6s %4s %8s %6s %8s %10s %-23s\n", + "OBJS", "ACTIVE", "USE", "OBJ SIZE", "SLABS", + "OBJ/SLAB", "CACHE SIZE", "NAME"); + + p = list = slabsort(list); + for (i = 0; i < nr_rows && p; i++) { + printf("%6lu %6lu %3lu%% %7.2fK %6lu %8lu %9luK %-23s\n", + p->nr_objs, p->nr_active_objs, p->use, + p->obj_size / 1024.0, p->nr_slabs, + p->objs_per_slab, + p->nr_pages * page_size, + p->name); + p = p->next; + } + + free_slablist(list); + + return 0; +} |