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,16 +27,94 @@ #include #include #include +#include +#include +#include +#include +#include #include "slab.h" -#include "procps.h" -#include "alloc.h" +#include "procps-private.h" -#define SLABINFO_LINE_LEN 2048 -#define SLABINFO_VER_LEN 100 -#define SLABINFO_FILE "/proc/slabinfo" +#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 -static struct slab_info *free_index; +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 */ +}; + +/* + * Zero out the slabnode data, keeping the memory allocated. + */ +static void slabnodes_clear( + struct procps_slabinfo *info) +{ + 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; +} + +/* Alloc up more slabnode memory, if required + */ +static int slabnodes_alloc( + struct procps_slabinfo *info) +{ + 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; +} /* * get_slabnode - allocate slab_info structures using a free list @@ -46,61 +124,26 @@ static struct slab_info *free_index; * both for simplicity and because the number of slab caches is fairly * constant. */ -static struct slab_info *get_slabnode(void) +static int get_slabnode( + struct procps_slabinfo *info, + struct procps_slabnode **node) { - struct slab_info *node; + int retval; - if (free_index) { - node = free_index; - free_index = free_index->next; - } else { - node = xmalloc(sizeof(struct slab_info)); - } + if (!info) + return -EINVAL; - return node; + if (info->nodes_used == info->nodes_alloc) { + if ((retval = slabnodes_alloc(info)) < 0) + return retval; + } + *node = &(info->nodes[info->nodes_used++]); + return 0; } -/* - * slab_badname_detect - return true if current slab was declared with - * whitespaces for instance - * FIXME :Other cases ? - */ - -static int slab_badname_detect(const char *restrict buffer) -{ - int numberarea=0; - while (*buffer){ - if((*buffer)==' ') - numberarea=1; - if(isalpha(*buffer)&&numberarea) - return 1; - buffer++; - } - return 0; -} - -/* - * 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.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(); + 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; + stats->min_obj_size = INT_MAX; + stats->max_obj_size = 0; - while (fgets(buffer, SLABINFO_LINE_LEN, f)) { - int assigned; + while (fgets(buffer, SLABINFO_LINE_LEN, info->slabinfo_fp )) { + if (buffer[0] == '#') + continue; - if (buffer[0] == '#') - continue; + if ((retval = get_slabnode(info, &node)) < 0) + return retval; - curr = get_slabnode(); - if (!curr) - break; + 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 (entries++ == 0) - *list = curr; - else - if (prev) - prev->next = curr; + 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; - 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); + node->cache_size = (unsigned long)node->nr_slabs * node->pages_per_slab + * page_size; - if (assigned < 8) { - fprintf(stderr, "unrecognizable data in slabinfo!\n"); - curr = NULL; - break; - } + if (node->nr_objs) { + node->use = 100 * node->nr_active_objs / node->nr_objs; + stats->nr_active_caches++; + } else + node->use = 0; - 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; + 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++; + } - curr->cache_size = (unsigned long)curr->nr_slabs * curr->pages_per_slab * page_size; + if (stats->nr_objs) + stats->avg_obj_size = stats->total_size / stats->nr_objs; - 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; + 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(); + 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; + stats->min_obj_size = INT_MAX; + stats->max_obj_size = 0; - while (fgets(buffer, SLABINFO_LINE_LEN, f)) { - int assigned; + while (fgets(buffer, SLABINFO_LINE_LEN, info->slabinfo_fp )) { + if (buffer[0] == '#') + continue; - curr = get_slabnode(); - if (!curr) - break; + if ((retval = get_slabnode(info, &node)) < 0) + return retval; - if (entries++ == 0) - *list = curr; - else - if (prev) - prev->next = curr; + 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; + } - 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 (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; - 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; - } + node->cache_size = (unsigned long)node->nr_slabs * + node->pages_per_slab * page_size; - 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; + if (node->nr_objs) { + node->use = 100 * node->nr_active_objs / node->nr_objs; + stats->nr_active_caches++; + } else + node->use = 0; - curr->cache_size = (unsigned long)curr->nr_slabs * curr->pages_per_slab * page_size; + if (node->obj_size) + node->objs_per_slab = node->pages_per_slab * + page_size / node->obj_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 += 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 (curr->obj_size) - curr->objs_per_slab = curr->pages_per_slab * - page_size / curr->obj_size; + if (stats->nr_objs) + stats->avg_obj_size = stats->total_size / stats->nr_objs; - 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; + 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 */ -static int parse_slabinfo10(struct slab_info **list, struct slab_stat *stats, - FILE *f) +PROCPS_EXPORT int procps_slabinfo_new( + struct procps_slabinfo **info) { - (void) list, (void) stats, (void) f; - fprintf(stderr, "slabinfo version 1.0 not yet supported\n"); - return 1; + 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 + */ +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) +{ + 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(): * - * 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. + * @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 */ -int get_slabinfo(struct slab_info **list, struct slab_stat *stats) +PROCPS_EXPORT char *procps_slabinfo_node_getname( + struct procps_slabinfo *info, + int nodeid) { - 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 == 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 + * + * Returns: number of nodes in @info or <0 on error + */ +PROCPS_EXPORT int procps_slabinfo_node_count( + const struct procps_slabinfo *info) +{ + 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 +__BEGIN_DECLS -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 */ +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, }; -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 */ +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 }; -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_slabinfo; +struct procps_slabnode; + +struct procps_slabinfo_result { + enum procps_slabinfo_stat item; + unsigned long result; + struct procps_slabinfo_result *next; +}; + +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; + 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; - } + 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(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)")); + 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); + 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 (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; - } + 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; + } } -static void parse_input(char c) +#if 0 + case 'Q': + delay = 0; + break; + } +} +#endif + +static void print_stats(struct procps_slabinfo *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; - } +#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 cleanup(const int is_tty, struct procps_slabinfo **slab_info) +{ + 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; + 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 } - }; + 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); + 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_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); + } + } - while ((o = getopt_long(argc, argv, "d:s:ohV", longopts, NULL)) != -1) { - switch (o) { - case 'd': - errno = 0; - delay = strtol_or_err(optarg, _("illegal delay")); - if (delay < 1) - xerrx(EXIT_FAILURE, - _("delay must be positive integer")); - break; - case 's': - sort_func = (int (*)(const struct slab_info*, - const struct slab_info *)) set_sort_func(optarg[0]); - break; - case 'o': - run_once=1; - delay = 0; - break; - case 'V': - printf(PROCPS_NG_VERSION); - return EXIT_SUCCESS; - case 'h': - usage(stdout); - default: - usage(stderr); - } - } + if (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); + is_tty = isatty(STDIN_FILENO); + if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) + xwarn(_("terminal setting retrieval")); - 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)); + old_rows = rows; + term_size(0); + if (!run_once) { + initscr(); + resizeterm(rows, cols); + signal(SIGWINCH, term_size); + } + signal(SIGINT, sigint_handler); - if (get_slabinfo(&slab_list, &stats)) { - retval = EXIT_FAILURE; - break; - } +#define STAT_VAL(e) result[e].result + do { + char *slab_name; + struct timeval tv; + fd_set readfds; + char c; + int i, myerrno; - if (!run_once && old_rows != rows) { - resizeterm(rows, cols); - old_rows = rows; - } + if (procps_slabinfo_read(slab_info) < 0) { + xwarn(_("Unable to read slabinfo")); + retval = EXIT_FAILURE; + break; + } - 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); + if (!run_once && old_rows != rows) { + resizeterm(rows, cols); + old_rows = rows; + } - slab_list = slabsort(slab_list); + move(0, 0); + print_stats(slab_info); - 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 (procps_slabinfo_sort(slab_info, sort_item) < 0) { + xwarn(_("Unable to sort slabnodes")); + retval= EXIT_FAILURE; + break; + } - 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; - } + 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); - 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 ((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); - if (is_tty) - tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty); - free_slabinfo(slab_list); - if (!run_once) - endwin(); - return retval; } 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"; + 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 - fSlab = fopen("/proc/slabinfo", "rb"); - if (!fSlab) { - xwarnx(_("your kernel does not support slabinfo or your permissions are insufficient")); - return; - } - 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;