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 <csmall@enc.com.au> Signed-off-by: Jim Warner <james.warner@comcast.net>
This commit is contained in:
parent
e445f7e6c5
commit
cf6c2155dc
@ -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;
|
||||
|
810
proc/slab.c
810
proc/slab.c
@ -27,16 +27,94 @@
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#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 <nodeallocs>. We don't use ": globalstat" part in both versions.
|
||||
*
|
||||
@ -130,225 +173,518 @@ void free_slabinfo(struct slab_info *list)
|
||||
* : globalstat <listallocs> <maxobjs> <grown> <reaped> <error> <maxfreeable> <freelimit> \
|
||||
* : cpustat <allochit> <allocmiss> <freehit> <freemiss>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
96
proc/slab.h
96
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 */
|
||||
|
608
slabtop.c
608
slabtop.c
@ -42,398 +42,324 @@
|
||||
#include "fileutils.h"
|
||||
#include "nls.h"
|
||||
#include "strutils.h"
|
||||
#include "proc/slab.h"
|
||||
#include "proc/version.h"
|
||||
#include <proc/slab.h>
|
||||
|
||||
#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 <secs> delay updates\n"), out);
|
||||
fputs(_(" -o, --once only display once, then exit\n"), out);
|
||||
fputs(_(" -s, --sort <char> 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 <secs> delay updates\n"), out);
|
||||
fputs(_(" -o, --once only display once, then exit\n"), out);
|
||||
fputs(_(" -s, --sort <char> 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;
|
||||
}
|
||||
|
83
vmstat.c
83
vmstat.c
@ -53,6 +53,7 @@
|
||||
#include <proc/vmstat.h>
|
||||
#include <proc/readstat.h>
|
||||
#include <proc/meminfo.h>
|
||||
#include <proc/slab.h>
|
||||
#include <proc/diskstat.h>
|
||||
|
||||
#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;
|
||||
|
Loading…
Reference in New Issue
Block a user