From: Craig Small Date: Sat, 4 Jul 2015 04:59:59 +0000 (-0500) Subject: library: rebase & make current initial slabinfo effort X-Git-Tag: v4.0.0~1097 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=cf6c2155dc20fe094aaf7ba5c23c393462111605;p=procps-ng library: rebase & make current initial slabinfo effort This was Craig's original patch, referenced below, but it was never pushed to newlib. It has now been rebased on top of some diskstat stuff to serve as a beginning. The original effort was perfectly serviceable (after a memory leak was fixed) but the approach would not have served future PID needs when that proc_t went bye bye. The slabtop requirements are similar to those of PIDs. One must accommodate the variable number of slab nodes (PIDs) while also accepting different data (char * and unsigned long). Furthermore, some generalized means to sort all that acquired stuff must somehow be provided. So I wanted to try a different approach that seemed to hold potential for satisfying future top and ps needs. Subsequent commits will make that attempt, building on Craig's original patch whose commit msg appears below. ------------------------------------------------------ All of the /proc/slabinfo related calls have been changed here. They follow the same procps_slabinfo_* format. Made both the slabtop and vmstat programs use the new API as one was using the old one and one was just sort of trying to do its own thing. Sorting of slabnodes is also possible via the library. Reference(s): http://www.freelists.org/post/procps/Sorting-slabsprocesses,3 http://www.freelists.org/post/procps/library-rework-slabinfo-calls Signed-off-by: Craig Small Signed-off-by: Jim Warner --- diff --git a/proc/libprocps.sym b/proc/libprocps.sym index f40cb52a..f72acb8d 100644 --- a/proc/libprocps.sym +++ b/proc/libprocps.sym @@ -6,17 +6,13 @@ global: escape_str; escape_strlist; escaped_copy; - free_slabinfo; freeproc; get_ns_id; get_ns_name; get_pid_digits; - get_slabinfo; - getslabinfo; look_up_our_self; lookup_wchan; openproc; - put_slabinfo; readeither; readproc; readproctab2; @@ -44,6 +40,17 @@ global: procps_meminfo_unref; procps_meminfo_get; procps_meminfo_get_chain; + procps_slabinfo_new; + procps_slabinfo_read; + procps_slabinfo_ref; + procps_slabinfo_unref; + procps_slabinfo_stat_get; + procps_slabinfo_stat_getchain; + procps_slabinfo_sort; + procps_slabinfo_node_count; + procps_slabinfo_node_get; + procps_slabinfo_node_getchain; + procps_slabinfo_node_getname; procps_stat_new; procps_stat_read; procps_stat_read_jiffs; diff --git a/proc/slab.c b/proc/slab.c index 444b79cc..a1e26c40 100644 --- a/proc/slab.c +++ b/proc/slab.c @@ -27,80 +27,123 @@ #include #include #include +#include +#include +#include +#include +#include #include "slab.h" -#include "procps.h" -#include "alloc.h" - -#define SLABINFO_LINE_LEN 2048 -#define SLABINFO_VER_LEN 100 -#define SLABINFO_FILE "/proc/slabinfo" - -static struct slab_info *free_index; +#include "procps-private.h" + +#define SLABINFO_LINE_LEN 2048 +#define SLABINFO_VER_LEN 100 +#define SLAB_INFO_NAME_LEN 128 +#define SLABINFO_FILE "/proc/slabinfo" +#define INITIAL_NODES 30 + +struct procps_slabnode { + char name[SLAB_INFO_NAME_LEN]; /* name of this cache */ + unsigned long cache_size; /* size of entire cache */ + unsigned nr_objs; /* number of objects in this cache */ + unsigned nr_active_objs; /* number of active objects */ + unsigned obj_size; /* size of each object */ + unsigned objs_per_slab; /* number of objects per slab */ + unsigned pages_per_slab; /* number of pages per slab */ + unsigned nr_slabs; /* number of slabs in this cache */ + unsigned nr_active_slabs; /* number of active slabs */ + unsigned use; /* percent full: total / active */ +}; + + +struct slabinfo_stats { + unsigned long total_size; /* size of all objects */ + unsigned long active_size; /* size of all active objects */ + unsigned nr_objs; /* number of objects, among all caches */ + unsigned nr_active_objs; /* number of active objects, among all caches */ + unsigned nr_pages; /* number of pages consumed by all objects */ + unsigned nr_slabs; /* number of slabs, among all caches */ + unsigned nr_active_slabs; /* number of active slabs, among all caches */ + unsigned nr_caches; /* number of caches */ + unsigned nr_active_caches; /* number of active caches */ + unsigned avg_obj_size; /* average object size */ + unsigned min_obj_size; /* size of smallest object */ + unsigned max_obj_size; /* size of largest object */ +}; + +struct procps_slabinfo { + int refcount; + FILE *slabinfo_fp; + struct slabinfo_stats stats; + struct procps_slabnode *nodes; /* first slabnode of this list */ + int nodes_alloc; /* nodes alloc()ed */ + int nodes_used; /* nodes using alloced memory */ +}; /* - * 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. + * Zero out the slabnode data, keeping the memory allocated. */ -static struct slab_info *get_slabnode(void) +static void slabnodes_clear( + struct procps_slabinfo *info) { - struct slab_info *node; - - if (free_index) { - node = free_index; - free_index = free_index->next; - } else { - node = xmalloc(sizeof(struct slab_info)); - } - - return node; + if (info == NULL || info->nodes == NULL || info->nodes_alloc < 1) + return; + memset(info->nodes, 0, sizeof(struct procps_slabnode)*info->nodes_alloc); + info->nodes_used = 0; } -/* - * slab_badname_detect - return true if current slab was declared with - * whitespaces for instance - * FIXME :Other cases ? +/* Alloc up more slabnode memory, if required */ - -static int slab_badname_detect(const char *restrict buffer) +static int slabnodes_alloc( + struct procps_slabinfo *info) { - int numberarea=0; - while (*buffer){ - if((*buffer)==' ') - numberarea=1; - if(isalpha(*buffer)&&numberarea) - return 1; - buffer++; - } - return 0; + struct procps_slabnode *new_nodes; + int new_count; + + if (info == NULL) + return -EINVAL; + + if (info->nodes_used < info->nodes_alloc) + return 0; + /* Increment the allocated number of slabs */ + new_count = info->nodes_alloc * 5/4+30; + + if ((new_nodes = realloc(info->nodes, + sizeof(struct procps_slabnode)*new_count)) == NULL) + return -ENOMEM; + info->nodes = new_nodes; + info->nodes_alloc = new_count; + return 0; } /* - * put_slabinfo - return all allocated nodes to the free list + * 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. */ -void put_slabinfo(struct slab_info *head) +static int get_slabnode( + struct procps_slabinfo *info, + struct procps_slabnode **node) { - free_index = head; -} + int retval; -/* - * 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; - } + if (!info) + return -EINVAL; + + if (info->nodes_used == info->nodes_alloc) { + if ((retval = slabnodes_alloc(info)) < 0) + return retval; + } + *node = &(info->nodes[info->nodes_used++]); + return 0; } -/* parse_slabinfo20 - actual parse routine for slabinfo 2.x (2.6 kernels) +/* parse_slabinfo20: + * + * sactual parse routine for slabinfo 2.x (2.6 kernels) * Note: difference between 2.0 and 2.1 is in the ": globalstat" part where version 2.1 * has extra column . We don't use ": globalstat" part in both versions. * @@ -130,225 +173,518 @@ void free_slabinfo(struct slab_info *list) * : globalstat \ * : cpustat */ -static int parse_slabinfo20(struct slab_info **list, struct slab_stat *stats, - FILE *f) +static int parse_slabinfo20( + struct procps_slabinfo *info) { - 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 - if (prev) - 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 = (unsigned long)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 += (unsigned long)curr->nr_objs * curr->obj_size; - stats->active_size += (unsigned long)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, "\rerror 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; + struct procps_slabnode *node; + char buffer[SLABINFO_LINE_LEN]; + int retval; + int page_size = getpagesize(); + struct slabinfo_stats *stats = &(info->stats); + + stats->min_obj_size = INT_MAX; + stats->max_obj_size = 0; + + while (fgets(buffer, SLABINFO_LINE_LEN, info->slabinfo_fp )) { + if (buffer[0] == '#') + continue; + + if ((retval = get_slabnode(info, &node)) < 0) + return retval; + + if (sscanf(buffer, + "%" STRINGIFY(SLAB_INFO_NAME_LEN) + "s %d %d %d %d %d : tunables %*d %*d %*d : \ + slabdata %d %d %*d", node->name, + &node->nr_active_objs, &node->nr_objs, + &node->obj_size, &node->objs_per_slab, + &node->pages_per_slab, &node->nr_active_slabs, + &node->nr_slabs) < 8) { + if (errno != 0) + return -errno; + return -EINVAL; + } + + if (node->obj_size < stats->min_obj_size) + stats->min_obj_size = node->obj_size; + if (node->obj_size > stats->max_obj_size) + stats->max_obj_size = node->obj_size; + + node->cache_size = (unsigned long)node->nr_slabs * node->pages_per_slab + * page_size; + + if (node->nr_objs) { + node->use = 100 * node->nr_active_objs / node->nr_objs; + stats->nr_active_caches++; + } else + node->use = 0; + + stats->nr_objs += node->nr_objs; + stats->nr_active_objs += node->nr_active_objs; + stats->total_size += (unsigned long)node->nr_objs * node->obj_size; + stats->active_size += (unsigned long)node->nr_active_objs * node->obj_size; + stats->nr_pages += node->nr_slabs * node->pages_per_slab; + stats->nr_slabs += node->nr_slabs; + stats->nr_active_slabs += node->nr_active_slabs; + stats->nr_caches++; + } + + 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) +static int parse_slabinfo11( + struct procps_slabinfo *info) { - 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 - if (prev) - 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 your slabinfo version 1.1\n\r"); - if(slab_badname_detect(buffer)) - fprintf(stderr, "Found an error in cache name at line %s\n", buffer); - 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 = (unsigned long)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 += (unsigned long)curr->nr_objs * curr->obj_size; - stats->active_size += (unsigned long)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, "\rerror 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; + struct procps_slabnode *node; + char buffer[SLABINFO_LINE_LEN]; + int retval; + int page_size = getpagesize(); + struct slabinfo_stats *stats = &(info->stats); + + stats->min_obj_size = INT_MAX; + stats->max_obj_size = 0; + + while (fgets(buffer, SLABINFO_LINE_LEN, info->slabinfo_fp )) { + if (buffer[0] == '#') + continue; + + if ((retval = get_slabnode(info, &node)) < 0) + return retval; + + if (sscanf(buffer, + "%" STRINGIFY(SLAB_INFO_NAME_LEN) + "s %d %d %d %d %d %d", + node->name, &node->nr_active_objs, + &node->nr_objs, &node->obj_size, + &node->nr_active_slabs, &node->nr_slabs, + &node->pages_per_slab) < 6) { + if (errno != 0) + return -errno; + return -EINVAL; + } + + if (node->obj_size < stats->min_obj_size) + stats->min_obj_size = node->obj_size; + if (node->obj_size > stats->max_obj_size) + stats->max_obj_size = node->obj_size; + + node->cache_size = (unsigned long)node->nr_slabs * + node->pages_per_slab * page_size; + + if (node->nr_objs) { + node->use = 100 * node->nr_active_objs / node->nr_objs; + stats->nr_active_caches++; + } else + node->use = 0; + + if (node->obj_size) + node->objs_per_slab = node->pages_per_slab * + page_size / node->obj_size; + + stats->nr_objs += node->nr_objs; + stats->nr_active_objs += node->nr_active_objs; + stats->total_size += (unsigned long)node->nr_objs * node->obj_size; + stats->active_size += (unsigned long)node->nr_active_objs * node->obj_size; + stats->nr_pages += node->nr_slabs * node->pages_per_slab; + stats->nr_slabs += node->nr_slabs; + stats->nr_active_slabs += node->nr_active_slabs; + stats->nr_caches++; + } + + 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) + * procps_slabinfo_new: * - * Not yet implemented. Please feel free. + * @info: location of returned new structure + * + * Returns: 0 on success <0 on failure + */ +PROCPS_EXPORT int procps_slabinfo_new( + struct procps_slabinfo **info) +{ + struct procps_slabinfo *si; + + if (info == NULL) + return -EINVAL; + + si = calloc(1, sizeof(struct procps_slabinfo)); + if (!si) + return -ENOMEM; + + si->refcount = 1; + si->slabinfo_fp = NULL; + si->nodes_alloc = 0; + si->nodes_used = 0; + si->nodes = NULL; + *info = si; + return 0; +} + +/* procps_slabinfo_read: + * + * Read the data out of /proc/slabinfo putting the information + * into the supplie info container + * + * Returns: 0 on success, negative on error */ -static int parse_slabinfo10(struct slab_info **list, struct slab_stat *stats, - FILE *f) +PROCPS_EXPORT int procps_slabinfo_read ( + struct procps_slabinfo *info) +{ + char line[SLABINFO_LINE_LEN]; + int retval, size, major, minor; + + if (info == NULL) + return -1; + + memset(&(info->stats), 0, sizeof(struct slabinfo_stats)); + if ((retval = slabnodes_alloc(info)) < 0) + return retval; + slabnodes_clear(info); + + if (NULL == info->slabinfo_fp && + (info->slabinfo_fp = fopen(SLABINFO_FILE, "r")) == NULL) + return -errno; + if (fseek(info->slabinfo_fp, 0L, SEEK_SET) < 0) + return -errno; + + /* Parse the version string */ + if (!fgets(line, SLABINFO_LINE_LEN, info->slabinfo_fp)) + return -errno; + + if (sscanf(line, "slabinfo - version: %d.%d", &major, &minor) != 2) + return -EINVAL; + + if (major == 2) + retval = parse_slabinfo20(info); + else if (major == 1 && minor == 1) + retval = parse_slabinfo11(info); + else + return -ERANGE; + return retval; +} + +PROCPS_EXPORT int procps_slabinfo_ref ( + struct procps_slabinfo *info) { - (void) list, (void) stats, (void) f; - fprintf(stderr, "slabinfo version 1.0 not yet supported\n"); - return 1; + if (info == NULL) + return -EINVAL; + info->refcount++; + return info->refcount; +} + +PROCPS_EXPORT int procps_slabinfo_unref ( + struct procps_slabinfo **info) +{ + if (info == NULL || *info == NULL) + return -EINVAL; + (*info)->refcount--; + if ((*info)->refcount == 0) { + if ((*info)->slabinfo_fp) { + fclose((*info)->slabinfo_fp); + (*info)->slabinfo_fp = NULL; + } + free(*info); + *info = NULL; + return 0; + } + return (*info)->refcount; +} + +PROCPS_EXPORT unsigned long procps_slabinfo_stat_get( + struct procps_slabinfo *info, + enum procps_slabinfo_stat item) +{ + if (!item) + return 0; + switch (item) { + case PROCPS_SLABINFO_OBJS: + return info->stats.nr_objs; + case PROCPS_SLABINFO_AOBJS: + return info->stats.nr_active_objs; + case PROCPS_SLABINFO_PAGES: + return info->stats.nr_pages; + case PROCPS_SLABINFO_SLABS: + return info->stats.nr_slabs; + case PROCPS_SLABINFO_ASLABS: + return info->stats.nr_active_slabs; + case PROCPS_SLABINFO_CACHES: + return info->stats.nr_caches; + case PROCPS_SLABINFO_ACACHES: + return info->stats.nr_active_caches; + case PROCPS_SLABINFO_SIZE_AVG: + return info->stats.avg_obj_size; + case PROCPS_SLABINFO_SIZE_MIN: + return info->stats.min_obj_size; + case PROCPS_SLABINFO_SIZE_MAX: + return info->stats.max_obj_size; + case PROCPS_SLABINFO_SIZE_TOTAL: + return info->stats.total_size; + case PROCPS_SLABINFO_SIZE_ACTIVE: + return info->stats.active_size; + } + return 0; +} + +PROCPS_EXPORT int procps_slabinfo_stat_getchain( + struct procps_slabinfo *info, + struct procps_slabinfo_result *result) +{ + if (!info || !result) + return -EINVAL; + + do { + switch (result->item) { + case PROCPS_SLABINFO_OBJS: + result->result = info->stats.nr_objs; + break; + case PROCPS_SLABINFO_AOBJS: + result->result = info->stats.nr_active_objs; + break; + case PROCPS_SLABINFO_PAGES: + result->result = info->stats.nr_pages; + break; + case PROCPS_SLABINFO_SLABS: + result->result = info->stats.nr_slabs; + break; + case PROCPS_SLABINFO_ASLABS: + result->result = info->stats.nr_active_slabs; + break; + case PROCPS_SLABINFO_CACHES: + result->result = info->stats.nr_caches; + break; + case PROCPS_SLABINFO_ACACHES: + result->result = info->stats.nr_active_caches; + break; + case PROCPS_SLABINFO_SIZE_AVG: + result->result = info->stats.avg_obj_size; + break; + case PROCPS_SLABINFO_SIZE_MIN: + result->result = info->stats.min_obj_size; + break; + case PROCPS_SLABINFO_SIZE_MAX: + result->result = info->stats.max_obj_size; + break; + case PROCPS_SLABINFO_SIZE_TOTAL: + result->result = info->stats.total_size; + break; + case PROCPS_SLABINFO_SIZE_ACTIVE: + result->result = info->stats.active_size; + break; + default: + return -EINVAL; + } + result = result->next; + } while(result); + return 0; } /* - * slabinfo - parse the system's slabinfo and fill out both a linked list of - * slab_info structures and the slab_stat structure + * procps_slabinfo_node_getname(): + * + * @info: slabinfo structure with data read in + * @nodeid: number of node we want the name for + * + * Find the name of the given node + * + * Returns: name or NULL on error + */ +PROCPS_EXPORT char *procps_slabinfo_node_getname( + struct procps_slabinfo *info, + int nodeid) +{ + if (info == NULL) + return NULL; + if (nodeid > info->nodes_used) + return NULL; + return info->nodes[nodeid].name; +} + +PROCPS_EXPORT int procps_slabinfo_node_getchain ( + struct procps_slabinfo *info, + struct procps_slabnode_result *result, + int nodeid) +{ + if (info == NULL || result == NULL) + return -EINVAL; + if (nodeid > info->nodes_used) + return -EINVAL; + + do { + switch (result->item) { + case PROCPS_SLABNODE_SIZE: + result->result = info->nodes[nodeid].cache_size; + break; + case PROCPS_SLABNODE_OBJS: + result->result = info->nodes[nodeid].nr_objs; + break; + case PROCPS_SLABNODE_AOBJS: + result->result = info->nodes[nodeid].nr_active_objs; + break; + case PROCPS_SLABNODE_OBJ_SIZE: + result->result = info->nodes[nodeid].obj_size; + break; + case PROCPS_SLABNODE_OBJS_PER_SLAB: + result->result = info->nodes[nodeid].objs_per_slab; + break; + case PROCPS_SLABNODE_PAGES_PER_SLAB: + result->result = info->nodes[nodeid].pages_per_slab; + break; + case PROCPS_SLABNODE_SLABS: + result->result = info->nodes[nodeid].nr_slabs; + break; + case PROCPS_SLABNODE_ASLABS: + result->result = info->nodes[nodeid].nr_active_slabs; + break; + case PROCPS_SLABNODE_USE: + result->result = info->nodes[nodeid].use; + break; + default: + return -EINVAL; + } + result = result->next; + } while(result); + return 0; +} + +/* + * Sorting functions + * + * These functions sort the slabnodes. The sort type is + * the same as the get enum + */ +static int sort_name(const void *a, const void *b) +{ + return strcmp( + ((struct procps_slabnode*)a)->name, + ((struct procps_slabnode*)b)->name); +} + +static int sort_objs(const void *a, const void *b) +{ + return ((struct procps_slabnode*)b)->nr_objs - + ((struct procps_slabnode*)a)->nr_objs; +} + +static int sort_aobjs(const void *a, const void *b) +{ + return ((struct procps_slabnode*)b)->nr_active_objs - + ((struct procps_slabnode*)a)->nr_active_objs; +} + +static int sort_size(const void *a, const void *b) +{ + return ((struct procps_slabnode*)b)->obj_size - + ((struct procps_slabnode*)a)->obj_size; +} + +static int sort_objsperslab(const void *a, const void *b) +{ + return ((struct procps_slabnode*)b)->objs_per_slab - + ((struct procps_slabnode*)a)->objs_per_slab; +} + +static int sort_pagesperslab(const void *a, const void *b) +{ + return ((struct procps_slabnode*)b)->pages_per_slab - + ((struct procps_slabnode*)a)->pages_per_slab; +} + +static int sort_slabs(const void *a, const void *b) +{ + return ((struct procps_slabnode*)b)->nr_slabs - + ((struct procps_slabnode*)a)->nr_slabs; +} + +static int sort_use(const void *a, const void *b) +{ + return ((struct procps_slabnode*)b)->use - + ((struct procps_slabnode*)a)->use; +} + +static int sort_aslabs(const void *a, const void *b) +{ + return ((struct procps_slabnode*)b)->nr_active_slabs - + ((struct procps_slabnode*)a)->nr_active_slabs; +} + +/* + * procps_slabinfo_sort: + * + * @info: the slabinfo that has the read data + * @item: slabnode item to sort by + * + * Sort the slabnodes contained in @info based + * upon the criteria @item + * + * Returns: 0 on success < on error + */ +PROCPS_EXPORT int procps_slabinfo_sort( + struct procps_slabinfo *info, + const enum procps_slabinfo_nodeitem item) +{ + void * sort_func = NULL; + + if (info == NULL) + return -EINVAL; + + switch (item) { + case PROCPS_SLABNODE_NAME: + sort_func = sort_name; + break; + case PROCPS_SLABNODE_OBJS: + sort_func = sort_objs; + break; + case PROCPS_SLABNODE_AOBJS: + sort_func = sort_aobjs; + break; + case PROCPS_SLABNODE_SIZE: + sort_func = sort_size; + break; + case PROCPS_SLABNODE_OBJS_PER_SLAB: + sort_func = sort_objsperslab; + break; + case PROCPS_SLABNODE_PAGES_PER_SLAB: + sort_func = sort_pagesperslab; + break; + case PROCPS_SLABNODE_SLABS: + sort_func = sort_slabs; + break; + case PROCPS_SLABNODE_ASLABS: + sort_func = sort_aslabs; + break; + case PROCPS_SLABNODE_USE: + sort_func = sort_use; + break; + default: + return -EINVAL; + } + qsort(info->nodes, info->nodes_used, + sizeof(struct procps_slabnode), sort_func); + return 0; +} + +/* + * procps_slabinfo_node_count(): + * + * @info: read in slabinfo 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. + * Returns: number of nodes in @info or <0 on error */ -int get_slabinfo(struct slab_info **list, struct slab_stat *stats) +PROCPS_EXPORT int procps_slabinfo_node_count( + const struct procps_slabinfo *info) { - FILE *slabfile; - char buffer[SLABINFO_VER_LEN]; - int major, minor, ret = 0; - - slabfile = fopen(SLABINFO_FILE, "r"); - if (!slabfile) { - perror("fopen " SLABINFO_FILE); - return 1; - } - - if (!fgets(buffer, SLABINFO_VER_LEN, slabfile)) { - fprintf(stderr, "cannot read from slabinfo\n"); - fclose(slabfile); - return 1; - } - - if (sscanf(buffer, "slabinfo - version: %d.%d", &major, &minor) != 2) { - fprintf(stderr, "not the good old slabinfo we know\n"); - fclose(slabfile); - return 1; - } - - if (major == 2) - 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"); - fclose(slabfile); - return 1; - } - - fclose(slabfile); - - return ret; + if (!info) + return -EINVAL; + return info->nodes_used; } diff --git a/proc/slab.h b/proc/slab.h index 09dbe00c..299544d4 100644 --- a/proc/slab.h +++ b/proc/slab.h @@ -1,39 +1,77 @@ #ifndef _PROC_SLAB_H #define _PROC_SLAB_H -#define SLAB_INFO_NAME_LEN 128 - -struct slab_info { - char name[SLAB_INFO_NAME_LEN]; /* name of this cache */ - struct slab_info *next; - unsigned long cache_size; /* size of entire cache */ - unsigned nr_objs; /* number of objects in this cache */ - unsigned nr_active_objs; /* number of active objects */ - unsigned obj_size; /* size of each object */ - unsigned objs_per_slab; /* number of objects per slab */ - unsigned pages_per_slab; /* number of pages per slab */ - unsigned nr_slabs; /* number of slabs in this cache */ - unsigned nr_active_slabs; /* number of active slabs */ - unsigned use; /* percent full: total / active */ +__BEGIN_DECLS + +enum procps_slabinfo_stat { + PROCPS_SLABINFO_OBJS, + PROCPS_SLABINFO_AOBJS, + PROCPS_SLABINFO_PAGES, + PROCPS_SLABINFO_SLABS, + PROCPS_SLABINFO_ASLABS, + PROCPS_SLABINFO_CACHES, + PROCPS_SLABINFO_ACACHES, + PROCPS_SLABINFO_SIZE_AVG, + PROCPS_SLABINFO_SIZE_MIN, + PROCPS_SLABINFO_SIZE_MAX, + PROCPS_SLABINFO_SIZE_TOTAL, + PROCPS_SLABINFO_SIZE_ACTIVE, +}; + +enum procps_slabinfo_nodeitem { + PROCPS_SLABNODE_NAME, + PROCPS_SLABNODE_SIZE, + PROCPS_SLABNODE_OBJS, + PROCPS_SLABNODE_AOBJS, + PROCPS_SLABNODE_OBJ_SIZE, + PROCPS_SLABNODE_OBJS_PER_SLAB, + PROCPS_SLABNODE_PAGES_PER_SLAB, + PROCPS_SLABNODE_SLABS, + PROCPS_SLABNODE_ASLABS, + PROCPS_SLABNODE_USE }; -struct slab_stat { - unsigned long total_size; /* size of all objects */ - unsigned long active_size; /* size of all active objects */ - unsigned nr_objs; /* number of objects, among all caches */ - unsigned nr_active_objs; /* number of active objects, among all caches */ - unsigned nr_pages; /* number of pages consumed by all objects */ - unsigned nr_slabs; /* number of slabs, among all caches */ - unsigned nr_active_slabs; /* number of active slabs, among all caches */ - unsigned nr_caches; /* number of caches */ - unsigned nr_active_caches; /* number of active caches */ - unsigned avg_obj_size; /* average object size */ - unsigned min_obj_size; /* size of smallest object */ - unsigned max_obj_size; /* size of largest object */ +struct procps_slabinfo; +struct procps_slabnode; + +struct procps_slabinfo_result { + enum procps_slabinfo_stat item; + unsigned long result; + struct procps_slabinfo_result *next; }; -extern void put_slabinfo(struct slab_info *); -extern void free_slabinfo(struct slab_info *); -extern int get_slabinfo(struct slab_info **, struct slab_stat *); +struct procps_slabnode_result { + enum procps_slabinfo_nodeitem item; + unsigned long result; + struct procps_slabnode_result *next; +}; + +int procps_slabinfo_new (struct procps_slabinfo **info); +int procps_slabinfo_read (struct procps_slabinfo *info); + +int procps_slabinfo_ref (struct procps_slabinfo *info); +int procps_slabinfo_unref (struct procps_slabinfo **info); + +unsigned long procps_slabinfo_stat_get (struct procps_slabinfo *info, + enum procps_slabinfo_stat item); + +int procps_slabinfo_stat_getchain (struct procps_slabinfo *info, + struct procps_slabinfo_result *result); + +int procps_slabinfo_sort( struct procps_slabinfo *info, + const enum procps_slabinfo_nodeitem item); + +int procps_slabinfo_node_count(const struct procps_slabinfo *info); + +int procps_slabinfo_node_get (struct procps_slabinfo *info, + struct procps_slabnode **node); +int procps_slabinfo_node_getchain (struct procps_slabinfo *info, + struct procps_slabnode_result *result, + int nodeid); + +char *procps_slabinfo_node_getname(struct procps_slabinfo *info, + int nodeid); +__END_DECLS + #endif /* _PROC_SLAB_H */ diff --git a/slabtop.c b/slabtop.c index ec7515de..8879c620 100644 --- a/slabtop.c +++ b/slabtop.c @@ -42,398 +42,324 @@ #include "fileutils.h" #include "nls.h" #include "strutils.h" -#include "proc/slab.h" -#include "proc/version.h" +#include -#define DEF_SORT_FUNC sort_nr_objs +#define DEFAULT_SORT_ITEM PROCPS_SLABNODE_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); -} +static int run_once = 0; +#define print_line(fmt, ...) if (run_once) printf(fmt, __VA_ARGS__); else printw(fmt, __VA_ARGS__) /* * 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; - } + 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; + 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 delay updates\n"), out); - fputs(_(" -o, --once only display once, then exit\n"), out); - fputs(_(" -s, --sort 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"), out); - fputs(_(" a: sort by number of active objects\n"), out); - fputs(_(" b: sort by objects per slab\n"), out); - fputs(_(" c: sort by cache size\n"), out); - fputs(_(" l: sort by number of slabs\n"), out); - fputs(_(" v: sort by number of active slabs\n"), out); - fputs(_(" n: sort by name\n"), out); - fputs(_(" o: sort by number of objects (the default)\n"), out); - fputs(_(" p: sort by pages per slab\n"), out); - fputs(_(" s: sort by object size\n"), out); - fputs(_(" u: sort by cache utilization\n"), out); - fprintf(out, USAGE_MAN_TAIL("slabtop(1)")); - - exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); + fputs(USAGE_HEADER, out); + fprintf(out, _(" %s [options]\n"), program_invocation_short_name); + fputs(USAGE_OPTIONS, out); + fputs(_(" -d, --delay delay updates\n"), out); + fputs(_(" -o, --once only display once, then exit\n"), out); + fputs(_(" -s, --sort 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"), out); + fputs(_(" a: sort by number of active objects\n"), out); + fputs(_(" b: sort by objects per slab\n"), out); + fputs(_(" c: sort by cache size\n"), out); + fputs(_(" l: sort by number of slabs\n"), out); + fputs(_(" v: sort by number of active slabs\n"), out); + fputs(_(" n: sort by name\n"), out); + fputs(_(" o: sort by number of objects (the default)\n"), out); + fputs(_(" p: sort by pages per slab\n"), out); + fputs(_(" s: sort by object size\n"), out); + fputs(_(" 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) +static enum procps_slabinfo_nodeitem get_sort_item( + const char key, + enum procps_slabinfo_nodeitem old_sort) +{ + switch (tolower(key)) { + case 'n': + return PROCPS_SLABNODE_NAME; + case 'o': + return PROCPS_SLABNODE_OBJS; + case 'a': + return PROCPS_SLABNODE_AOBJS; + case 's': + return PROCPS_SLABNODE_SIZE; + case 'b': + return PROCPS_SLABNODE_OBJS_PER_SLAB; + case 'p': + return PROCPS_SLABNODE_PAGES_PER_SLAB; + case 'l': + return PROCPS_SLABNODE_SLABS; + case 'v': + return PROCPS_SLABNODE_ASLABS; + case 'c': + return PROCPS_SLABNODE_SIZE; + case 'u': + return PROCPS_SLABNODE_USE; + default: + return old_sort; + } +} + +#if 0 + case 'Q': + delay = 0; + break; + } +} +#endif + +static void print_stats(struct procps_slabinfo *info) { - 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; - } +#define STAT_VAL(e) stats[e].result + enum stat_enums { + stat_AOBJS, stat_OBJS, stat_ASLABS, stat_SLABS, + stat_ACACHES, stat_CACHES, stat_ACTIVE, stat_TOTAL, + stat_MIN, stat_AVG, stat_MAX, + }; + static struct procps_slabinfo_result stats[] = { + { PROCPS_SLABINFO_AOBJS, 0, &stats[1] }, + { PROCPS_SLABINFO_OBJS, 0, &stats[2] }, + { PROCPS_SLABINFO_ASLABS, 0, &stats[3] }, + { PROCPS_SLABINFO_SLABS, 0, &stats[4] }, + { PROCPS_SLABINFO_ACACHES, 0, &stats[5] }, + { PROCPS_SLABINFO_CACHES, 0, &stats[6] }, + { PROCPS_SLABINFO_SIZE_ACTIVE, 0, &stats[7] }, + { PROCPS_SLABINFO_SIZE_TOTAL, 0, &stats[8] }, + { PROCPS_SLABINFO_SIZE_MIN, 0, &stats[9] }, + { PROCPS_SLABINFO_SIZE_AVG, 0, &stats[10] }, + { PROCPS_SLABINFO_SIZE_MAX, 0, NULL }, + }; + + if (procps_slabinfo_stat_getchain(info, stats) < 0) + xerrx(EXIT_FAILURE, + _("Error getting slabinfo results")); + + 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. */ + /* xgettext:no-c-format */ + _("Active / Total Objects (% used)"), + STAT_VAL(stat_AOBJS), STAT_VAL(stat_OBJS), + 100.0 * STAT_VAL(stat_AOBJS) / STAT_VAL(stat_OBJS), + /* xgettext:no-c-format */ + _("Active / Total Slabs (% used)"), + STAT_VAL(stat_ASLABS), STAT_VAL(stat_SLABS), + 100.0 * STAT_VAL(stat_ASLABS) / STAT_VAL(stat_SLABS), + /* xgettext:no-c-format */ + _("Active / Total Caches (% used)"), + STAT_VAL(stat_ACACHES), STAT_VAL(stat_CACHES), + 100.0 * STAT_VAL(stat_ACACHES) / STAT_VAL(stat_CACHES), + /* xgettext:no-c-format */ + _("Active / Total Size (% used)"), + STAT_VAL(stat_ACTIVE) / 1024.0 , STAT_VAL(stat_TOTAL) / 1024.0, + 100.0 * STAT_VAL(stat_ACTIVE) / STAT_VAL(stat_TOTAL), + _("Minimum / Average / Maximum Object"), + STAT_VAL(stat_MIN) / 1024.0, STAT_VAL(stat_AVG) / 1024.0, + STAT_VAL(stat_MAX) / 1024.0); +#undef STAT_VAL } -static void parse_input(char c) +static void cleanup(const int is_tty, struct procps_slabinfo **slab_info) { - 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; - } + if (is_tty) + tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty); + if (!run_once) + endwin(); + procps_slabinfo_unref(slab_info); } -#define print_line(fmt, ...) if (run_once) printf(fmt, __VA_ARGS__); else printw(fmt, __VA_ARGS__) int main(int argc, char *argv[]) { - int is_tty, 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 } - }; + int is_tty, o; + int nr_slabs; + unsigned short old_rows; + int retval = EXIT_SUCCESS; + struct procps_slabinfo *slab_info; + enum procps_slabinfo_nodeitem sort_item = DEFAULT_SORT_ITEM; + + 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 } + }; + + struct procps_slabnode_result result[] = { + { PROCPS_SLABNODE_OBJS, 0, &result[1] }, + { PROCPS_SLABNODE_AOBJS, 0, &result[2] }, + { PROCPS_SLABNODE_USE, 0, &result[3] }, + { PROCPS_SLABNODE_OBJ_SIZE, 0, &result[4] }, + { PROCPS_SLABNODE_SLABS, 0, &result[5] }, + { PROCPS_SLABNODE_OBJS_PER_SLAB, 0, &result[6] }, + { PROCPS_SLABNODE_SIZE, 0, NULL } }; + enum result_enums { + stat_OBJS, stat_AOBJS, stat_USE, stat_OSIZE, stat_SLABS, + stat_OPS, stat_SIZE }; #ifdef HAVE_PROGRAM_INVOCATION_NAME - program_invocation_name = program_invocation_short_name; + program_invocation_name = program_invocation_short_name; #endif - setlocale (LC_ALL, ""); - bindtextdomain(PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - atexit(close_stdout); - - 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); - } - } - - is_tty = isatty(STDIN_FILENO); - if (is_tty && 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. */ - /* xgettext:no-c-format */ - _("Active / Total Objects (% used)"), - stats.nr_active_objs, stats.nr_objs, - 100.0 * stats.nr_active_objs / stats.nr_objs, - /* xgettext:no-c-format */ - _("Active / Total Slabs (% used)"), - stats.nr_active_slabs, stats.nr_slabs, - 100.0 * stats.nr_active_slabs / stats.nr_slabs, - /* xgettext:no-c-format */ - _("Active / Total Caches (% used)"), - stats.nr_active_caches, stats.nr_caches, - 100.0 * stats.nr_active_caches / stats.nr_caches, - /* xgettext:no-c-format */ - _("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(STDIN_FILENO, &c, 1) != 1) - break; - parse_input(c); - } - } - } while (delay); - - if (is_tty) - tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty); - free_slabinfo(slab_list); - if (!run_once) - endwin(); - return retval; + setlocale (LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + 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_item = get_sort_item(optarg[0], sort_item); + 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 (procps_slabinfo_new(&slab_info) < 0) + xerrx(EXIT_FAILURE, + _("Unable to create slabinfo structure")); + + + is_tty = isatty(STDIN_FILENO); + if (is_tty && 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); + +#define STAT_VAL(e) result[e].result + do { + char *slab_name; + struct timeval tv; + fd_set readfds; + char c; + int i, myerrno; + + if (procps_slabinfo_read(slab_info) < 0) { + xwarn(_("Unable to read slabinfo")); + retval = EXIT_FAILURE; + break; + } + + if (!run_once && old_rows != rows) { + resizeterm(rows, cols); + old_rows = rows; + } + + move(0, 0); + print_stats(slab_info); + + if (procps_slabinfo_sort(slab_info, sort_item) < 0) { + xwarn(_("Unable to sort slabnodes")); + retval= EXIT_FAILURE; + break; + } + + 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); + + if ((nr_slabs = procps_slabinfo_node_count(slab_info)) < 0) { + xwarn(_("Unable to count slabinfo nodes")); + retval = EXIT_FAILURE; + break; + } + + for (i=0 ; i < rows - 8 && i < nr_slabs; i++) { + if (procps_slabinfo_node_getchain(slab_info, result, i) < 0) { + xwarn(_("Unable to get slabinfo node data")); + retval = EXIT_FAILURE; + break; + } + slab_name= procps_slabinfo_node_getname(slab_info, i); + print_line("%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n", + STAT_VAL(stat_OBJS), STAT_VAL(stat_AOBJS), + STAT_VAL(stat_USE), + STAT_VAL(stat_OSIZE) / 1024.0, STAT_VAL(stat_SLABS), + STAT_VAL(stat_OPS), + (unsigned)(STAT_VAL(stat_SIZE) / 1024), + slab_name?slab_name:"(unknown)"); + } + 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(STDIN_FILENO, &c, 1) != 1) + break; + if (c == 'Q' || c == 'q') + delay = 0; + else + sort_item = get_sort_item(c, sort_item); + } + } + } while (delay); + cleanup(is_tty, &slab_info); + } diff --git a/vmstat.c b/vmstat.c index 6bd124b5..3cd4f1c3 100644 --- a/vmstat.c +++ b/vmstat.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #define UNIT_B 1 @@ -613,45 +614,52 @@ static void slabheader(void) static void slabformat(void) { - FILE *fSlab; - struct slab_cache *slabs; - unsigned long nSlab, i, j, k; - const char format[] = "%-24s %6u %6u %6u %6u\n"; - - fSlab = fopen("/proc/slabinfo", "rb"); - if (!fSlab) { - xwarnx(_("your kernel does not support slabinfo or your permissions are insufficient")); - return; - } + struct procps_slabinfo *slab_info; + int i, nodeid, nr_slabs; + const char format[] = "%-24s %6u %6u %6u %6u\n"; + char *slab_name; + struct procps_slabnode_result result[] = { + { PROCPS_SLABNODE_AOBJS, 0, &result[1] }, + { PROCPS_SLABNODE_OBJS, 0, &result[2] }, + { PROCPS_SLABNODE_OBJ_SIZE, 0, &result[3] }, + { PROCPS_SLABNODE_OBJS_PER_SLAB, 0, NULL }}; + enum result_enums { + stat_AOBJS, stat_OBJS, stat_OSIZE, stat_OPS}; +#define SLAB_VAL(e) result[e].result - if (!moreheaders) - slabheader(); - nSlab = getslabinfo(&slabs); - for (k = 0; k < nSlab; k++) { - if (moreheaders && ((k % height) == 0)) - slabheader(); - printf(format, - slabs[k].name, - slabs[k].active_objs, - slabs[k].num_objs, - slabs[k].objsize, slabs[k].objperslab); - } - free(slabs); - for (j = 1, k = 1; infinite_updates || j < num_updates; j++) { - sleep(sleep_time); - nSlab = getslabinfo(&slabs); - for (i = 0; i < nSlab; i++, k++) { - if (moreheaders && ((k % height) == 0)) - slabheader(); - printf(format, - slabs[i].name, - slabs[i].active_objs, - slabs[i].num_objs, - slabs[i].objsize, slabs[i].objperslab); - } - free(slabs); - } - fclose(fSlab); + + if (procps_slabinfo_new(&slab_info) < 0) + xerrx(EXIT_FAILURE, + _("Unable to create slabinfo structure")); + + + if (!moreheaders) + slabheader(); + + for (i = 0; infinite_updates || i < num_updates; i++) { + if (procps_slabinfo_read(slab_info) < 0) + xerrx(EXIT_FAILURE, + _("Unable to read slabinfo structure")); + if ((nr_slabs = procps_slabinfo_node_count(slab_info)) < 0) + xerrx(EXIT_FAILURE, + _("Unable to count number of slabinfo nodes")); + + for (nodeid = 0; nodeid < nr_slabs; nodeid++) { + if (moreheaders && ((nodeid % height) == 0)) + slabheader(); + if (procps_slabinfo_node_getchain(slab_info, result, nodeid) < 0) + xerrx(EXIT_FAILURE, + _("Error getting slabinfo results")); + slab_name = procps_slabinfo_node_getname(slab_info, nodeid); + + printf(format, + slab_name?slab_name:"(unknown)", + SLAB_VAL(stat_AOBJS), SLAB_VAL(stat_OBJS), + SLAB_VAL(stat_OSIZE), SLAB_VAL(stat_OPS)); + } + if (infinite_updates || i+1 < num_updates) + sleep(sleep_time); + } } static void disksum_format(void) @@ -914,6 +922,7 @@ int main(int argc, char *argv[]) sleep_time = tmp; infinite_updates = 1; } + num_updates = 1; if (optind < argc) { num_updates = strtol_or_err(argv[optind++], _("failed to parse argument")); infinite_updates = 0;